上篇问题及Spring AOP实现原理浅析
上篇说了一个AOP编程问题,那是一个错误的AOP案例。它的错误在A类中,再次粘贴A类代码:
@Component
public class AImpl implements A {
public void doing() {
System.out.println("hello");
}
public static void main( String[] args )
{
ApplicationContext ctx=new ClassPathXmlApplicationContext("bean.xml");
AImpl a=ctx.getBean(AImpl.class); //(1)出错行
//A a=ctx.getBean(A.class); //(2)换成这样就正确了
a.doing();
}
}
先说一下正确代码(代码2)的执行过程:
步骤 | 执行过程 |
---|---|
1 | 程序启动,生成Bean并加载到缓存池(因为Spring默认是即时加载bean,而不是延时加载),因为配置了切面,所以,不生成AImpl类的bean,而生成一个proxy类对象。这个proxy类实现了A接口,并通过反射机制来调用AIplm类对象来完成操作。(这其实就是动态代理模式) |
2 | 得到beanFactory。 |
3 | 根据A的名以单例模式获取bean实例,发现容器中只有proxy对象是A类对象,所以实际返回的是proxy对象 |
4 | 执行proxy对象的doing()方法. |
为什么行(1)错了呢?
因为,容器中并没有AImpl类的bean实例,而只有proxy类的实例。所以,ctx.getBean(AImpl.class) 返回的是null。
Spring AOP是默认是通过动态代理的方式来实现的,动态代理要求代理对象和被代理对象必须要有同一个接口。如果类一个接口都没有实现,那么,Spring将使用CGLIB方式实现,CGLIB方式不要求类实现接口。
上面的示例中,Aimpl实现了A接口,所以使用的是动态代理方式,如果Aimpl不实现A接口,则将会使用GCLIB方式,那么行(1)也是正确的。
入门实例
1.加入依赖包,建议使用maven的方式。修改pom..xml文件,加入下面的依赖。
<!-- spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
<!-- AspectJ依賴包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
2.bean.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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--自动扫描包中的bean,使用注解来配置bean就需要这一行,需要修改包名-->
<context:component-scan base-package="com.lcl.springlearning"></context:component-scan>
<!-- 添加对 aspectj支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
3.编写正常的逻辑代码。一个接口A,和一个实现类AImpl。Spring AOP的动态代理机制要求被切面拦截的类必须实现一个接口。
接口:
public interface A {
public void doing();
}
实现:
@Component
public class AImpl implements A {
public void doing() {
System.out.println("hello");
}
}
4.定义切面。注意这个切面必须定义成一个bean,也就是要加。上@Component
注解
@Aspect //定义一个切面
@Component
public class B {
@Pointcut("execution(* doing(..))") //定义一个切点,拦截所有的doing方法
public void pointCutMethod(){
}
@Before("pointCutMethod()") //定义前置事件
public void doBefore(){
System.out.println("前置通知!");
}
@After("pointCutMethod()") //定义最终事件
public void doAfter(){
System.out.println("后置通知!");
}
}
5、测试代码
public static void main( String[] args )
{
ApplicationContext ctx=new ClassPathXmlApplicationContext("bean.xml");
A a=ctx.getBean(A.class);//注意要使用接口A
a.doing();
}
刚开始学的时候,看了很多实例都讲了太多现实逻辑,非常难懂,强烈要求讲例子的时候不要加入太多现实逻辑。