您的位置:首页 > 编程语言 > Java开发

Spring总结3—AOP

2016-04-24 17:45 567 查看

一、AOP概念及原理

1、AOP概念:

定义:将程序中的交叉业务逻辑提取出来,称之为切面。将这些切面动态织入到目标对象,然后生成一个代理对象的过程。

AOP(Aspect-Oriented Programming,面向切面编程)是一种程序设计思想,该思想的主要目标是将系统分为两部分:一部分封装了系统中各组件的通用功能(罗辑或者责任),形成一个切面,该切面被称为应用系统的”横切关注点”,此部分包含了与系统核心商业逻辑关系不大的部分;系统的其余部分,即核心商业逻辑部分,被称为系统的”核心关注点”,此部分包含了系统中业务处理的主要流程。其中,”横切关注点”经常出现在”核心关注点”的周围,但无论出现在何处,它们的基本功能是相似的,比如权限认证、日志、事务处理等。

2、面向切面编程思维的好处

一方面可以使我们在系统开发过程中的责任分工更为明确(比如,可以让高级工程师负责”横切关注点”的开发,初级工程师负责”核心关注点”的开发,配置工程师负责将以上两部分搭建成一个完整的应用系统);另一方面,清晰的系统结构将使我们以后的系统维护工作变得更为轻松。

总述:可以动态的添加和删除在切面上的逻辑而不影响原来的执行代码。

3、工作原理—动态代理

动态代理原理:动态代理原理其实就是反射+多态+聚合的实现。JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。

具体实现案例:

创建代理器,让其实现InvocationHandler接口

public class LogInterceptor implements InvocationHandler{
//log日志要添加一个目标
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public void beforeMethod(Method method){
System.out.println(method.getName()+"start!");
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
beforeMethod(method);
method.invoke(target, args);
return null;
}
}


测试该代理类:关键是Proxy.newProxyInstance()里面有3个参数,分别是被代理目标对象的ClassLoader,被代理目标对象实现的接口,InvocationHandler类型的代理器。

public void testProxy(){
//代理的目标对象(面向抽象编程:UserDAOPImpl()是抽象类UserDAP的实现类)
UserDAO userDAO = new UserDAOImpl();
//代理器(实现了InvocationHandler接口)
LogInterceptor log = new LogInterceptor();
//在代理器中注入代理对象
log.setTarget(userDAO);
//执行代码任务,转换成目标对象
UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),userDAO.getClass().getInterfaces(), (InvocationHandler) log);
System.out.println(userDAOProxy.getClass());
//调用目标对象的方法,(保存用户)
userDAOProxy.save(new User());
}


AOP采用动态代理的过程:

将切面使用动态代理的方式动态织入到目标对象(被代理类),形成一个代理对象;

目标对象如果没有实现代理接口,那么Spring会采用CGLib生成代理对象,该代理对象是目标对象的子类;

目标对象是final类的并且也没有实现代理接口的,就不能运用AOP

二、AOP基础知识

a)JoinPoint(连接点)

连接点,指切面可以织入到目标对象的位置(方法,属性等).例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个Join point。

b)PointCut(切入点)

切入点,指通知应用到哪些类的哪些方法或属性之上的规则。本质上是一个捕获连接点的结构(或称连接点的集合)。在AOP中,可以定义一个Point cut,来捕获相关方法(通知中的逻辑)的调用。

c)Aspect(切面)

Point cut和Advice的组合,它类似于OOP中定义的一个类,但它代表的更多的是对象间横向的关系。

d)Advice(通知)

Point cut的执行代码,它是执行”切面”的具体逻辑。

e)Target(目标对象)

指需要织入切面的对象。包含Join point的对象,它是被Advice的类,也是商务逻辑对象。这个对象永远是一个被代理的对象。

f)Weave(织入)

织入,指将通知插入到目标对象。将Aspec模块与核心商务逻辑(即目标对象)进行关联的过程,比如,将事务处理模块和银行柜员机程序通过配置结合起来,决定什么情况下事务处理模块被通知调用。

g)Proxy(AOP代理)

代理对象,指切面织入目标对象之后形成的对象。由AOP框架创建的对象,它是真正执行Advice的实体。

三、AOP配置—Annotation

1、xml文件中首先引入

xmlns:aop=”http://www.springframework.org/schema/aop”以及其对应的xsi:schemaLocation。

2、再添加aop注解配置

<!--aop面向切面编程注解必须添加上下面的语句-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>下:


此时就可以解析对应的Annotation了。

配置文件代码如下:

<?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-2.5.xsd    http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!--注解annotation-->
<context:annotation-config />
<!--Component:在类之上添加@Component注解,意味着是一个成员,默认名字是类名首字母小写,可以指定名字@Component("")
对于需注入的成员,添加@Resource(name=""),即可注入成功
@Service @Controller @Repository 与@Component相同意义。-->
<context:component-scan base-package="com.cdd"/>
<!--aop面向切面编程注解必须添加上下面的语句-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>


3、建立我们的拦截类

4、用@Aspect注解这个类

拦截类就是我们要作为切面的那个类,即穿插到我们的业务逻辑中的类。比如监听或是打印日志

5、建立处理方法

拦截类中建立处理方法,具体方法的实现。

6、用@Before、@After、@AfterThrowing(“”)、@Around来注解对应的方法,规定该处理方法在切入点执行的位置

7、写明白切入点(execution …….)

对于每个方法的切入点都要进行规定。例如注释在某方法上@Before ,即表示:在织入点(execution)方法之前执行该方法

@Before("execution(public void com.cdd.dao.impl.UserDAOImpl.save(com.cdd.model.User))")//织入点到具体某个类的具有某个返回值的方法之前。


若织入点是某个包下的任意子包的具有任意返回值的任意方法 之前都执行该方法,织入点命名如下

@Before("execution(public * com.cdd..*.*(..))")
public void before(){
System.out.println("method before!");
}


8、让spring对我们的拦截器类进行管理@Component

完整代码如下:

@Aspect    //2.声明是一个切面
@Component //1.首先声明该类是个成员
public class LogInterceptor_AOP {
//a).@Before 在织入点方法(execution)之前执行该方法。
//@Before("execution(public void com.cdd.dao.impl.UserDAOImpl.save(com.cdd.model.User))")//3.织入点

//b)若在某包下的任意子包下的具有任意返回值的任意方法 之前都执行该方法,织入点命名如下
@Before("execution(public * com.cdd..*.*(..))") public void before(){ System.out.println("method before!"); }

//AfterReturning()是指织入点方法执行正常且完成后执行该方法
@AfterReturning("execution(public * com.cdd..*.*(..))")
public void afterReturn(){
System.out.println("after returning!");
}
//若每个方法的织入点相同,则可以将织入点定义为PointCut
@Pointcut("execution(public * com.cdd..*.*(..))")
public void myPointCut(){}

//@Around 环绕通知。环绕通知在一个方法执行之前和之后执行。它使得通知有机会 在一个方法执行之前和执行之后运行
//必须有ProceedingJoinPoint参数。执行pjp.proceed()方法
@Around("myPointCut()")
public void aroundMethod(ProceedingJoinPoint pjp){
System.out.println("pjp around before");
try {
pjp.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("pjp around after");
}
//@AfterThrowing("")抛出异常通知在一个方法抛出异常后执行
//@After 不论一个方法是如何结束的,最终通知都会运行。通常用它来释放资源
}


四、AOP配置—xml

1、把interceptor对象初始化

建立拦截类

public class LogInterceptor_XML {
public void before(){
System.out.println("method before~!");
}
}


2、xml配置,设置切面、切入点以及切入位置

<?xml version="1.0" encoding="UTF-8"?>
<beans ...此处省略,与注解相同>
<context:annotation-config />
<context:component-scan base-package="com.cdd" />
<bean id="logInterceptor" class="com.cdd.aop.LogInterceptor_XML">
</bean>
<!--AOP xml配置-->
<aop:config>
<!--全局的PointCut -->
<aop:pointcut expression="execution(public * com.cdd.service..*.add(..))" id="servicePointCut"/>
<aop:aspect id="logAspect" ref="logInterceptor">
<!--有个before方法,其属性method:执行切面的哪个方法  织入点引用全局的,也可引用局部定义的,也可以直接指定pointcut-->
<!--<aop:pointcut expression="execution(public * com.cdd.service..*.add(..))" id="logPointCut"/>-->
<aop:before method="before" pointcut-ref="servicePointCut"/>
</aop:aspect>
</aop:config>
</beans>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: