下面这个系列是非常好的例子:
http://www.yiibai.com/spring/spring-3-hello-world-example.html
正在看,把一些基础夯实。
IoC可以从下面一篇开始看(前面几篇的bean配置也需要看,是基础):
http://www.yiibai.com/spring/spring-dependency-injection-di.html
AOP可以从下面一篇开始看:
http://www.yiibai.com/spring/spring-aop-examples-advice.html
开始写代码实验各种效果:
实验1(简单利用ApplicationContext)http://www.yiibai.com/spring/spring-3-hello-world-example.html:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.myapp</groupId> <artifactId>springdemo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.2.RELEASE</version> </dependency> </dependencies> </project>
resources里面方applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="name" value="Here"/> </bean> </beans>
src/main/java里面建package:com.myapp.springdemo,然后有两个class,分别是
Hello.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public class Hello { private String name; public void setName(String name) { this.name = name; } public void printHello() { System.out.printf("Hello is %s ", name); } }
SpringDemo.java
package com.myapp.springdemo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by baidu on 16/10/20. */ public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello obj = (Hello) context.getBean("helloBean"); obj.printHello(); } }
然后运行,成功:
十月 20, 2016 2:27:20 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 14:27:20 CST 2016]; root of context hierarchy 十月 20, 2016 2:27:20 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] Hello is Here Process finished with exit code 0
实验2(松耦合,通过xml配置class之间的依赖关系)http://www.yiibai.com/spring/spring-loosely-coupled-example.html
pom.xml不变。
resources/applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator" ref="CsvOutputGenerator"/> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
还有另一种写法,把property的ref单独提出来:
<bean id="helloBean" class="com.myapp.springdemo.Hello">
<property name="outputGenerator">
<ref local="CsvOutputGenerator"/>
</property>
</bean>
上面的local也可以写成bean。
其实,无论是在相同或不同的XML文件,“ref” 标签可以访问一个bean,但是,对于该项目的可读性,如果引用了相同的 XML文件中声明 bean,您应该使用“local”属性。
SpringDemo.java
package com.myapp.springdemo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by baidu on 16/10/20. */ public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello obj = (Hello) context.getBean("helloBean"); obj.printHello(); } }
Hello.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public class Hello { private IOutputGenerator outputGenerator; public void setOutputGenerator(IOutputGenerator outputGenerator) { this.outputGenerator = outputGenerator; } public void printHello() { this.outputGenerator.print(); } }
IOutputGenerator.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public interface IOutputGenerator { public void print(); }
CsvOutputGenerator.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public class CsvOutputGenerator implements IOutputGenerator{ public void print() { System.out.printf("Here is csv output "); } }
运行,成功。
十月 20, 2016 2:42:20 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 14:42:20 CST 2016]; root of context hierarchy 十月 20, 2016 2:42:21 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] Here is csv output Process finished with exit code 0
下面,再试一下AOP的内容:
在Spring AOP中,有 4 种类型通知(advices)的支持: 通知(Advice)之前 - 该方法执行前运行 通知(Advice)返回之后 – 运行后,该方法返回一个结果 通知(Advice)抛出之后 – 运行方法抛出异常后, 环绕通知 – 环绕方法执行运行,结合以上这三个通知。
实验1(在方法前执行)http://www.yiibai.com/spring/spring-aop-examples-advice.html:
增加一个 HijackMethod.java,注意是要实现MethodBeforeAdvice接口的方法:
package com.myapp.springdemo; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /** * Created by baidu on 16/10/20. */ public class HijackMethod implements MethodBeforeAdvice{ public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("HijackBeforeMethod"); } }
其他的文件,为了实验需要,稍作修改。
CsvOutputGenerator.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public class CsvOutputGenerator implements IOutputGenerator{ public void printHello() { System.out.printf("Here is csv output "); } public void printBye() { System.out.printf("Byebye in csv output "); } }
Hello.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public class Hello { private IOutputGenerator outputGenerator; public void setOutputGenerator(IOutputGenerator outputGenerator) { this.outputGenerator = outputGenerator; } public void print() { System.out.println("*************************"); this.outputGenerator.printHello(); System.out.println("*************************"); this.outputGenerator.printBye(); System.out.println("*************************"); } }
IOutputGenerator.java
package com.myapp.springdemo; /** * Created by baidu on 16/10/20. */ public interface IOutputGenerator { public void printHello(); public void printBye(); }
在applicationContext.xml里面需要进行配置:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator"> <ref local="CsvOutputGenerator"/> </property> </bean> <bean id="hijackMethodBean" class="com.myapp.springdemo.HijackMethod"/> <bean id="helloProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="helloBean"/> <property name="interceptorNames"> <list> <value>hijackMethodBean</value> </list> </property> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
然后主文件的调用部分修改成调用Proxy:
package com.myapp.springdemo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by baidu on 16/10/20. */ public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //Hello obj = (Hello) context.getBean("helloBean"); Hello obj = (Hello) context.getBean("helloProxyBean"); obj.print(); } }
运行,得到结果:
十月 20, 2016 3:11:11 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 15:11:11 CST 2016]; root of context hierarchy 十月 20, 2016 3:11:11 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] HijackBeforeMethod ************************* Here is csv output ************************* Byebye in csv output ************************* Process finished with exit code 0
这时候,发现"HijackBeforeMethod"打印在了"***"前面。这时候发现,我们的AoP是做在了Hello这个class,所以是在Hello的方法print之前进行了Advice的调用。修改一下xml配置:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator"> <ref local="outputProxyBean"/> </property> </bean> <bean id="hijackMethodBean" class="com.myapp.springdemo.HijackMethod"/> <bean id="outputProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="CsvOutputGenerator"/> <property name="interceptorNames"> <list> <value>hijackMethodBean</value> </list> </property> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
然后在主函数里面把调用改回原来的class:
package com.myapp.springdemo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by baidu on 16/10/20. */ public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //Hello obj = (Hello) context.getBean("helloBean"); Hello obj = (Hello) context.getBean("helloBean"); obj.print(); } }
运行,得到结果:
十月 20, 2016 3:19:34 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 15:19:34 CST 2016]; root of context hierarchy 十月 20, 2016 3:19:34 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] ************************* HijackBeforeMethod Here is csv output ************************* HijackBeforeMethod Byebye in csv output ************************* Process finished with exit code 0
实验2,环绕AOP,包括了前、后、异常的处理,http://www.yiibai.com/spring/spring-aop-examples-advice.html 最后一部分。
增加一个 HijackAroundMethod.java,需要实现 MethodInterceptor接口的方法,而不是之前的MethodBeforeAdvice接口的方法:
package com.myapp.springdemo; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import java.util.Arrays; import java.util.IllegalFormatCodePointException; /** * Created by baidu on 16/10/20. */ public class HijackAroundMethod implements MethodInterceptor{ public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("Method name: " + methodInvocation.getMethod().getName()); System.out.println("Method args: " + Arrays.toString(methodInvocation.getArguments())); System.out.println("Before Method"); try { Object result = methodInvocation.proceed(); System.out.println("After Method"); return result; } catch (IllegalArgumentException e) { System.out.println("Catch Exception for Method"); throw e; } } }
然后其他的java代码都不用变,只需要修改applicationContext.xml配置文件里面关于拦截器Interceptor的bean映射class的内容,只需要改一行:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator"> <ref local="outputProxyBean"/> </property> </bean> <bean id="hijackMethodBean" class="com.myapp.springdemo.HijackAroundMethod"/> <bean id="outputProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="CsvOutputGenerator"/> <property name="interceptorNames"> <list> <value>hijackMethodBean</value> </list> </property> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
只需要修改上面飘红的那一行。然后运行,得到:
十月 20, 2016 3:29:10 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 15:29:10 CST 2016]; root of context hierarchy 十月 20, 2016 3:29:10 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] ************************* Method name: printHello Method args: [] Before Method Here is csv output After Method ************************* Method name: printBye Method args: [] Before Method Byebye in csv output After Method ************************* Process finished with exit code 0
实验3,只对部分函数进行拦截,http://www.yiibai.com/spring/spring-aop-example-pointcut-advisor.html 后面一部分:
修改xml配置,要用到"org.springframework.aop.support.NameMatchMethodYiibaicut"(新版已经改成NameMatchMethodPointcut)
在Spring AOP中,有三个非常专业术语- Advices, Yiibaicut , Advisor,把它在非官方的方式...
Advice – 指示之前或方法执行后采取的行动。
Yiibaicut – 指明哪些方法应该拦截,通过方法的名称或正则表达式模式。
Advisor – 分组"通知"和”切入点“成为一个单元,并把它传递到代理工厂对象。
要对部分函数进行拦截,就要用到上面的概念。只需要修改xml配置文件即可,不需要改代码:
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator"> <ref local="outputProxyBean"/> </property> </bean> <bean id="hijackMethodBean" class="com.myapp.springdemo.HijackAroundMethod"/> <bean id="customerYiibaicut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="printBye"/> </bean> <bean id="outputAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="customerYiibaicut"/> <property name="advice" ref="hijackMethodBean"/> </bean> <bean id="outputProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="CsvOutputGenerator"/> <property name="interceptorNames"> <list> <value>outputAdvisor</value> </list> </property> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
在上面的文件中,可以看出,增加了一个"NameMatchMethodPointcut"的bean,再增加一个"DefaultPointcutAdvisor"的bean把这个Pointcut的过滤条件,和之前的advice “hijackMethodBean”关联起来。然后在“ProxyFactoryBean”里面把之前的advice换成这个advisor即可。
运行,得到结果:
十月 20, 2016 3:50:57 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 15:50:57 CST 2016]; root of context hierarchy 十月 20, 2016 3:50:57 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] ************************* Here is csv output ************************* Method name: printBye Method args: [] Before Method Byebye in csv output After Method ************************* Process finished with exit code 0
如果要拦截多个方法,xml里面可以这样写:
<bean id="customerYiibaicut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>printHello</value>
<value>printBye</value>
</list>
</property>
</bean>
运行,得到结果:
十月 20, 2016 3:55:54 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 15:55:54 CST 2016]; root of context hierarchy 十月 20, 2016 3:55:54 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] ************************* Method name: printHello Method args: [] Before Method Here is csv output After Method ************************* Method name: printBye Method args: [] Before Method Byebye in csv output After Method ************************* Process finished with exit code 0
或者可以用patter匹配的方法(注意,改的是Advisor,而Pointcut可以不用了):
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="helloBean" class="com.myapp.springdemo.Hello"> <property name="outputGenerator"> <ref local="outputProxyBean"/> </property> </bean> <bean id="hijackMethodBean" class="com.myapp.springdemo.HijackAroundMethod"/> <bean id="outputAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*print.*</value> </list> </property> <property name="advice" ref="hijackMethodBean"/> </bean> <bean id="outputProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="CsvOutputGenerator"/> <property name="interceptorNames"> <list> <value>outputAdvisor</value> </list> </property> </bean> <bean id="CsvOutputGenerator" class="com.myapp.springdemo.CsvOutputGenerator"/> </beans>
在上面文件中,使用pattern advisor的话,原来的"NameMatchPointcut"的bean不用了可以删去;创建一个RegexMethodPointcutAdvisor的bean,里面的property方面不再使用上个例子上的pointcut,而是配上新的property:patterns,注意patterns里面写的".*print.*"前面的.*也一定要加上,不加上的话不能匹配,可能是因为在方法名前面有调用的前缀吧,虽然打印出来的方法名称的确从print开始。运行,得到结果:
十月 20, 2016 4:01:59 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7a07c5b4: startup date [Thu Oct 20 16:01:59 CST 2016]; root of context hierarchy 十月 20, 2016 4:01:59 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [applicationContext.xml] ************************* Method name: printHello Method args: [] Before Method Here is csv output After Method ************************* Method name: printBye Method args: [] Before Method Byebye in csv output After Method ************************* Process finished with exit code 0
(下面一小段可以忽略)
再回顾一下之前Pointcut的例子,对一个class里面的部分函数进行AOP,参考网页里面的配置:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="customerService" class="com.yiibai.customer.services.CustomerService"> <property name="name" value="Yiibai" /> <property name="url" value="http://www.yiibai.com" /> </bean> <bean id="hijackAroundMethodBeanAdvice" class="com.yiibai.aop.HijackAroundMethod" /> <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="customerService" /> <property name="interceptorNames"> <list> <value>customerAdvisor</value> </list> </property> </bean> <bean id="customerYiibaicut" class="org.springframework.aop.support.NameMatchMethodYiibaicut"> <property name="mappedName" value="printName" /> </bean> <bean id="customerAdvisor" class="org.springframework.aop.support.DefaultYiibaicutAdvisor"> <property name="pointcut" ref="customerYiibaicut" /> <property name="advice" ref="hijackAroundMethodBeanAdvice" /> </bean> </beans>
里面还有一些其他的例子,比如Lucence:
http://www.yiibai.com/html/java/
最后,给一下全部目录:
Spring教程 Spring hello world实例 Spring松耦合实例 Spring JavaConfig实例 Spring JavaConfig @Import实例 Spring依赖注入(DI) Spring使用Setter依赖注入 Spring通过构造方法依赖注入 Spring Bean引用例子 如何注入值到Spring bean属性 Spring bean加载多个配置文件 Spring内部bean实例 Spring Bean作用域实例 Spring集合 (List,Set,Map,Properties) 实例 Spring ListFactoryBean实例 Spring SetFactoryBean实例 Spring MapFactoryBean例子 Spring注入日期到bean属性-CustomDateEditor Spring PropertyPlaceholderConfigurer实例 Spring bean配置继承 Spring依赖检查 Spring使用@Required注解依赖检查 Spring自定义@Required-style注解 Spring Bean InitializingBean和DisposableBean实例 Spring Bean init-method 和 destroy-method实例 Spring @PostConstruct和@PreDestroy实例 Spring EL hello world实例 Spring EL bean引用实例 Spring EL方法调用实例 Spring EL运算符实例 Spring EL三元运算(if-then-else)实例 Spring EL Lists,Maps实例 Spring EL正则表达式实例 Spring自动扫描组件 Spring过滤器组件自动扫描 Spring自动装配Beans Spring AOP通知实例 – Advice Spring AOP实例(Pointcut,Advisor) Spring自动代理创建者实例 Spring AOP+AspectJ注解实例 Spring Object到XML映射实例 Spring+JDBC实例 Spring JdbcTemplate+JdbcDaoSupport实例 Spring JdbcTemplate查询实例 Spring SimpleJdbcTemplate查询示例 Spring SimpleJdbcTemplate类命名参数实例 Spring+Hibernate+MySql实例 Spring AOP在Hibernate事务管理 Spring在bean配置文件中定义电子邮件模板 Spring发送带附件邮件 Spring依赖注入servlet会话监听器 Spring资源捆绑ResourceBundleMessageSource示例 Spring初学快速入门 Spring+JDBC实例