spring顾问包装通知
前边说到了顾问就是通知,没有实践,这里就实践一下,证明一下。
虽然可以说顾问就是通知,但是他们还是有的一定的区别的,通知是将目标类中所有的方法都进行的增强,而顾问却可以指定到特定的方法上,也就是说顾问的功能更加强大一些
而包装的方式常用的有两种,一种是基于名字的(方法),一种是基于正则的(方法),他们都是通过目标类的方法名进行的包装。
创建增强类
1 /** 2 * 增强类 3 */ 4 public class Advice implements MethodBeforeAdvice, AfterReturningAdvice { 5 @Override 6 public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { 7 System.out.println("我是后置增强"); 8 } 9 10 @Override 11 public void before(Method method, Object[] objects, Object o) throws Throwable { 12 System.out.println("我是前置增强"); 13 } 14 }
创建service接口
1 public interface DoSome { 2 void dosome(); 3 void say(); 4 }
创建实现类
public class DoSomeImpl implements DoSome{ public void dosome(){ System.out.println("我是service层"); } @Override public void say() { System.out.println("你好,bdqn"); } }
由于两种增强方式的service层和增强类都是一样的这里我只写一次
第一种根据方法名包装
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 <!--创建service层的bean--> 6 <bean id="service" class="service.DoSomeImpl"></bean> 7 <!--创建通知的bean--> 8 <bean id="advice" class="advice.Advice"></bean> 9 <!--顾问包装通知--> 10 <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> 11 <property name="advice" ref="advice"></property> 12 <property name="mappedNames" value="say"></property> 13 </bean> 14 <!--创建代理工厂bean,并注入属性--> 15 <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> 16 <!--注入service实现类的bean--> 17 <property name="target" ref="service"/> 18 <!--注入通知的bean,注意这里不使用ref注入,只能是value属性,属性为avcisor 的bean的id属性--> 19 <property name="interceptorNames" value="advisor"/> 20 </bean> 21 </beans>
创建测试类
public class Dome { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); DoSome proxyFactory = (DoSome) context.getBean("factoryBean");//这里注入的是ProxyFactoryBean的id属性值 proxyFactory.say(); System.out.println(">>>>>>>>>>>两个方法的分割线>>>>>>>>>>>>"); proxyFactory.dosome(); } }
第二种基于正则的方式
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 <!--创建service层的bean--> 6 <bean id="service" class="service.DoSomeImpl"></bean> 7 <!--创建通知的bean--> 8 <bean id="advice" class="advice.Advice"></bean> 9 <!--顾问包装通知--> 10 <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 11 <property name="advice" ref="advice"></property> 12 <property name="pattern" value=".*say.*"/> 13 </bean> 14 <!--创建代理工厂bean,并注入属性--> 15 <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> 16 <!--注入service实现类的bean--> 17 <property name="target" ref="service"/> 18 <!--注入通知的bean,注意这里不使用ref注入,只能是value属性,属性为通知类bean的id属性--> 19 <property name="interceptorNames" value="advisor"/> 20 </bean> 21 </beans>
测试类与第一种方式相同,结果自行验证,需要的pom节点
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.1.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.1.0.RELEASE</version> </dependency> <!--以上4个是spring的核心--> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.2</version> </dependency>
每次写一个通知都需要手动去添加一个代理工厂也是很烦人的,那自然有简单的方式,只需要配置一次即可以了
基于id名的代理生成器:
<?xml version="1.0" encoding="UTF-8"?> <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.xsd"> <!--创建service层的bean--> <bean id="service" class="service.DoSomeImpl"></bean> <!--创建通知的bean--> <bean id="advice" class="advice.Advice"></bean> <!--基于方法名的包装--> <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="advice"></property> <property name="mappedName" value="say"></property> </bean> <!--基于方法名的代理生成器--> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="service"></property> <property name="interceptorNames" value="advisor"/> </bean> </beans>
默认的代理生成器
<?xml version="1.0" encoding="UTF-8"?> <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.xsd"> <!--创建service层的bean--> <bean id="service" class="service.DoSomeImpl"></bean> <!--创建通知的bean--> <bean id="advice" class="advice.Advice"></bean> <!--顾问包装通知--> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice" ref="advice"></property> <property name="pattern" value=".*say.*"/> </bean> <!--基于方法名的代理生成器--> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <property name="interceptorNames" value="service"/> <property name="beanName" value="advisor"/> </bean> </beans>
由于代理工厂也被代理了,所以没有了代理工厂的id这里测试类需要做一定的改变
public class Dome { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml"); DoSome proxyFactory = (DoSome) context.getBean("service"); proxyFactory.say(); System.out.println(">>>>>>>>>>>两个方法的分割线>>>>>>>>>>>>"); proxyFactory.dosome(); } }
自动代理生成器推荐使用第一种,第二种容易出bug,因为它默认会为xml文件中的所有bean节点创建工厂