【Spring in Action】Spring的AOP基础知识及切面运用
2017-08-28 16:34
417 查看
前言:
大概在前两章Spring in Action的文章中写了Spring的AOP的简单运用,以及Spring依赖注入DI和Bean的装配各种不同的套路。一、温故知新:
一种场景正好温习一下:一个接口多个实现的时候。如下:1、接口Performance
package com.concert; import org.aspectj.lang.annotation.Pointcut; public interface Performance { void perform(); }
2、实现一:PerformanceImpl
package com.concert; import org.springframework.stereotype.Component; @Component public class PerformanceImpl implements Performance { public PerformanceImpl() { } @Override public void perform() { System.out.println("----------执行perform中----------"); } }3、实现二:Singer
package com.concert; import org.springframework.stereotype.Component; @Component public class Singer implements Performance { @Override public void perform() { System.out.println("---------我是一个演唱家Singer,我正在表演perform----------"); } }
4、JavaConfig自动装配:SpringConfig
package com.concert; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan public class SpringConfig { }5、测试用例:Test001
package com.concert; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=SpringConfig.class) public class Test001 { @Autowired private Performance pe; @Test public void test() { pe.perform(); } }
6、大概会看到这样一个错误:(意思是找到了两个符合要求的匹配项,机器不知道选哪个所以抛出异常了)
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.concert.Test001': Unsatisfied dependency expressed through field 'pe': No qualifying bean
of type [com.concert.Performance] is defined: expected single matching bean but found 2: performanceImpl,singer;
7、解决方案就是:自己告诉机器指定一个实现类。SpringConfig去掉@ComponentScan注解。然后修改后的如下:
package com.concert; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class SpringConfig { @Bean public Performance getPerformance(){ return new PerformanceImpl(); } }
8、我这里指定PerformanceImpl。好吧,这都不是重点。这期的重点是AOP切面。
二、AOP切面的使用:
1、写一个日志类:加@Aspect,加@Before("execution(* com.concert.Performance.perform(..))")等:package com.concert; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class Log { @Before("execution(* com.concert.Performance.perform(..))") public void beforeLog() { System.out.println("前置日志----beforeLog"); } @After("execution(* com.concert.Performance.perform(..))") public void afterLog() { System.out.println("后置日志----afterLog"); } }
2、光写了Log日志类,还只是一个普通的java 对象(POJO)。还需要在配置中生成Log对象,设置自动扫描@EnableAspectJAutoProxy。
package com.concert; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class SpringConfig { @Bean public Performance getPerformance(){ return new PerformanceImpl(); } @Bean public Log log(){ return new Log(); } }
3、再次执行测试代码Test001执行结果:
前置日志----beforeLog
----------执行perform中----------
后置日志----afterLog
三、总结分析:
1.切点是什么?切点:com.concert.Performance.perform(..),凡是实现这个接口的实例调用perform方法都会执行。2.使用切点每次都写一长串,execution(* com.xmlconcert.Performance.perform(..))简化如下:
package com.xmlconcert; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log { @Pointcut("execution(* com.xmlconcert.Performance.perform(..))") public void perform(){ } @Before("perform()") public void beforeLog() { System.out.println("前置日志----beforeLog"); } @After("perform()") public void afterLog() { System.out.println("后置日志----afterLog"); } }
3.切面是什么?Log类是切面。
4.如何设置自动代理?当前是通过JavaConfig的方式,对SpringConfig添加了@EnableAspectJAutoProxy,并在容器中生成了切面的Bean(Pojo)。当然也可以通过xml方式配置。xml配置思路同JavaConfig:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:component-scan base-package="com.xmlconcert"></context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> <!-- 声明bean --> <bean class="com.xmlconcert.Log"></bean> </beans>然后删除SpringConfig,删除这个因为是通过xml配置,排除干扰。删除Singer,是因为自动扫描,一对多的话还是会报异常。就是最上面说的异常。修改Test001:
package com.xmlconcert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test001 { @Autowired private Performance pe; @Test public void test() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("com/xmlconcert/applicationContext.xml"); pe = ctx.getBean(Performance.class); pe.perform(); ctx.close(); } }由于我换了一个com.xmlconcert,之前是com.concert,所以替换掉其中的所有。包括Log的切面代码:
package com.xmlconcert; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log { @Before("execution(* com.xmlconcert.Performance.perform(..))") public void beforeLog() { System.out.println("前置日志----beforeLog"); } @After("execution(* com.xmlconcert.Performance.perform(..))") public void afterLog() { System.out.println("后置日志----afterLog"); } }执行结果:
前置日志----beforeLog
----------执行perform中----------
后置日志----afterLog
5、环绕通知:修改Log
package com.xmlconcert; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log { @Pointcut("execution(* com.xmlconcert.Performance.perform(..))") public void perform(){ } // @Before("perform()") // public void beforeLog() { // System.out.println("前置日志----beforeLog"); // } // @After("perform()") // public void afterLog() { // System.out.println("后置日志----afterLog"); // } // 创建环绕通知 @Around("perform()") public void watchPerformance(ProceedingJoinPoint jp){ try { System.out.println("电话静音,坐下"); jp.proceed(); } catch (Throwable e) { e.printStackTrace(); System.out.println("演出失败,退款"); } } }执行结果:
电话静音,坐下
----------执行perform中----------
其中入参ProceedingJoinPoint,jp.proceed()是执行玩环绕通知后执行perform方法。如果不使用,则不执行perform。perform被拦截了。
其中System.out.println("演出失败,退款");是在抛出异常的时候执行。例如null指针异常。
6、处理通知中的参数:
Performance
package com.xmlconcert; import org.aspectj.lang.annotation.Pointcut; public interface Performance { void perform(); void performArgs(int arg); }
Log
package com.xmlconcert; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log { @Pointcut("execution(* com.xmlconcert.Performance.performArgs(int)) && args(num)") public void perform(int num){ } @Before("perform(num)") public void beforeLog(int num) { System.out.println("前置日志----beforeLog"+num); } @After("perform(num)") public void afterLog(int num) { System.out.println("后置日志----afterLog"+num); } // 创建环绕通知 // @Around("perform(num)") // public void watchPerformance(ProceedingJoinPoint jp){ // try { // System.out.println("电话静音,坐下"); // jp.proceed(); // } catch (Throwable e) { // e.printStackTrace(); // System.out.println("演出失败,退款"); // } // } }
Test001
package com.xmlconcert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test001 { @Autowired private Performance pe; @Test public void test() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("com/xmlconcert/applicationContext.xml"); pe = ctx.getBean(Performance.class); pe.performArgs(100001); ctx.close(); } }执行结果:
前置日志----beforeLog100001
----------执行performArgs中----------100001
后置日志----afterLog100001
7、这个例子可能还不明显,由书中的例子改的举例如下:
Player
package demo; public interface Player { void play(String song,int num); }CDPlayer
package demo; import org.springframework.stereotype.Component; @Component public class CDPlayer implements Player { @Override public void play(String song, int num) { System.out.println("歌曲名:"+song+" 播放次数:"+num); } }
AopLog
package demo; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class AopLog { //计数器 private int numCounter = 0; @Pointcut("execution(* demo.Player.play(String,int))&&args(song,num)") public void play(String song,int num){ } @Before("play(song,num)") public void before(String song,int num){ numCounter = numCounter+num; } public void getNum(){ System.out.println("CDPlayer共播放"+numCounter+"次"); } }xmlplayer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:component-scan base-package="demo"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean class="demo.AopLog"></bean> </beans>TestDemo
package demo; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestDemo { @Autowired private Player p; @Autowired private AopLog a; @Test public void test() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("demo/xmlplayer.xml"); p = ctx.getBean(Player.class); p.play("周杰伦的歌", 1); p.play("周杰伦的歌", 2); p.play("周杰伦的歌", 3); p.play("周杰伦的歌", 4); a = ctx.getBean(AopLog.class); a.getNum(); ctx.close(); } }执行结果:
歌曲名:周杰伦的歌 播放次数:1
歌曲名:周杰伦的歌 播放次数:2
歌曲名:周杰伦的歌 播放次数:3
歌曲名:周杰伦的歌 播放次数:4
播放10次
四、Spring AOP 使用接口注入为API引入新功能:
Performerpackage newfunc; public interface Performer { void play(); }
MisZhang
package newfunc; import org.springframework.stereotype.Component; @Component public class MisZhang implements Performer { @Override public void play() { //张女士是表演者 System.out.println("唱青藏高原"); } }applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="miszhang" class="newfunc.MisZhang"></bean> </beans>Test001
package newfunc; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test001 { private Performer p; @Test public void test() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("newfunc/applicationContext.xml"); p = (Performer) ctx.getBean("miszhang"); p.play(); ctx.close(); } }
执行结果:
唱青藏高原
需求:现在想对张女士MisZhang.class类,新增一个角色Artister接口。即由原本:张女士是一个唱青藏高原的人-----》升级为:张女士是一个会唱青藏高原的女艺术家。但是不允许对MisZhang.class做任何修改。
Artister
package newfunc; public interface Artister { void honor(); }
DefaultArtister
package newfunc; import org.springframework.stereotype.Component; @Component public class DefaultArtister implements Artister { @Override public void honor() { System.out.println("--------艺术家光环普照----------"); } }新增切面
EncoreableIntroducer
package newfunc; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclareParents; @Aspect public class EncoreableIntroducer { @DeclareParents(value="newfunc.MisZhang+",defaultImpl=DefaultArtister.class) public static Artister artister; }
修改applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <aop:aspectj-autoproxy> </aop:aspectj-autoproxy> <bean id="miszhang" class="newfunc.MisZhang"></bean> <bean class="newfunc.DefaultArtister"></bean> <bean class="newfunc.EncoreableIntroducer"></bean> </beans>
修改Test001
package newfunc; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test001 { private Performer p; private Artister a; @Test public void test() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("newfunc/applicationContext.xml"); p = (Performer) ctx.getBean("miszhang"); p.play(); a = (Artister) ctx.getBean("miszhang"); a.honor(); ctx.close(); } }执行结果:
唱青藏高原
--------艺术家光环普照----------
over--------
相关文章推荐
- Spring in Action 入门之面向切面编程AOP
- 【Spring in action】依赖注入DI和AOP切面
- (Spring In Action)bean注入基础知识
- SPRING IN ACTION 第4版笔记-第四章ASPECT-ORIENTED SPRING-005-定义切面使用@Aspect、@EnableAspectJAutoProxy、<aop:aspectj-autoproxy>
- Spring in Action 入门之面向切面编程AOP
- Spring In Action(三):基于注解的AOP
- 【Spring in action】SpringMVC的准备工作:Servlet基础
- Spring In Action(一)DI、AOP
- spring aop基础知识
- Spring基础配置(AOP切面编程)
- Spring实战(第4版) Spring Inaction 笔记(第一章)依赖注入和AOP
- Spring in Action 学习笔记三-AOP
- Spring AOP的一些基础知识
- [Java]Spring AOP基础知识-动态代理
- Spring 切面 AOP基础 之二
- Spring 切面 AOP基础 之三
- Spring AOP基础知识
- SPRING IN ACTION 第4版笔记-第四章ASPECT-ORIENTED SPRING-012-AOP总结
- Spring 切面 AOP基础 之一
- SpringInAction:在Spring中应用切面