Spring AOP
3、AOP(Aspect Oriented Programming):面向方面的编程
3.1、静态代理
当需要为一些成熟的项目增加日志时,需要修改到原有代码,此时可以为原有的对象创建一个静态代理
类,在这个代理类中实现相应的日志或者权限控制操作。
使用静态代理带来的最大问题,需要为每一个类都加入相应的控制代码,这些代码其实是和业务逻辑没有
关系的。
3.2、动态代理
动态代理可以将一些横切性的问题(日志管理、权限控制)提取出来成为一个模块,之后在运行的时候根
据需要切入到原有正常代码中。
步骤:
1、创建动态代理类
创建一个类实现InvocationHandler接口。以后创建对象就完全通过这个代理类进行创建。
2、在代理类创建相应的要代理的对象
/**
* 1、写一个实现InvocationHandler接口
* @author PM
*
*/
public class LogProxy implements InvocationHandler {
private LogProxy() {}
//2、创建一个代理对象
private Object target;
3、创建一个静态的getInstance方法来创建代理对象
//3、创建一个方法来生成对象,这个方法的参数是要代理的对象,getInstance所返回的对象就是代理对象
public static Object getInstance(Object o) {
//3.1、创建LogProxy对象
LogProxy proxy = new LogProxy();
//3.2、设置这个代理对象
proxy.target = o;
//3.3、通过Proxy的方法创建代理对象,第一个参数是要代理对象的classLoader,
//第二个参数是要代理对象实现的所有接口,第三个参数是实现了InvocationHandler的对象
//此时的result就是一个代理对象,代理的是o
Object result = Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(), proxy);
return result;
}
4、创建了代理对象之后,这个代理对象在执行任何方法时都会先执行invoke方法,就可以在这个方法中
加入控制信息。
/**
* 当有了代理对象之后,不管这个代理对象执行什么方法,都会调用以下的invoke方法
* @param proxy是代理对象
* @param method表示要执行的方法
* @param args表示要执行方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// if(method.getName().equals("add")||method.getName().equals("delete")) {
// Logger.info("进行了相应的操作");
// }
//obj表示方法执行之后的返回值
//method.invoke()通过反射来执行方法
Object obj = method.invoke(target, args);
//函数执行完成之后返回之后才执行
if(method.isAnnotationPresent(LogInfo.class)) {
LogInfo li = method.getAnnotation(LogInfo.class);
Logger.info(li.value());
}
return obj;
}
5、根据以上步骤之后,日志管理就被提取成为一个独立的模块,在运行时进行加入。
6、由于使用的getInstance这个static的方法注入对象,所以无法使用Annotation,需要使用xml的配置文
件。
<bean id="userDynamicDao" class="org.pm.spring.proxy.LogProxy" factory-method="getInstance">
<constructor-arg ref="userDao"/>
</bean>
<!-- factory-method="getInstance":创建对象的静态方法 -->
<bean id="messageDynamicDao" class="org.pm.spring.proxy.LogProxy" factory-method="getInstance">
<!-- 为getInstance这个静态方法传入参数,传入的参数是什么类型,
代理出来的对象就是什么类型 -->
<constructor-arg ref="messageDao"/>
</bean>
service中通过代理类注入对象:
@Resource(name="messageDynamicDao")
public void setMessageDao(IMessageDao messageDao) {
this.messageDao = messageDao;
}
public IUserDao getUserDao() {
return userDao;
}
//默认通过名称注入,在JSR330中提供了@Inject来注入
@Resource(name="userDynamicDao")
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
7、此时可以考虑为相应的方法增加Annotation来增强相应的操作。
7.1、创建Annotation:
@Retention(RetentionPolicy.RUNTIME)
public @interface LogInfo {
public String value() default"";
}
7.2、为接口方法增加Annotation:
public interface IUserDao {
@LogInfo("添加用户信息")
public void add(User user);
@LogInfo("删除用户信息")
public void delete(int id);
public User load(int id);
}
3.3、使用Spring实现AOP
Spring就是使用动态代理来实现AOP的。
1、 基于Annotation的实现:
1.1、在xml中打开aop的自动检索
<!-- 打开基于Annotation的AOP -->
<aop:aspectj-autoproxy/>
1.2、创建一个需要实现动态代理的类
@Component("logAspect") //让这个切面类被Spring所管理
@Aspect //声明这个类是一个切面类
public class LogAspect {
/**
* execution(* org.pm.spring.dao.*.add*(..))
* 第一个*表示任意返回值
* 第二个*表示org.pm.spring.dao包中的所有类
* 第三个*表示以add开头的所有方法
* (..)表示任意参数
*/
@Before("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logStart(JoinPoint jp) {
//得到执行的对象
System.out.println(jp.getTarget());
//得到执行的方法
System.out.println(jp.getSignature().getName());
Logger.info("加入日志");
}
/**
* 函数调用完成之后执行
* @param jp
*/
@After("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logEnd(JoinPoint jp) {
Logger.info("方法调用结束加入日志");
}
/**
* 函数调用中执行
* @param pjp
* @throws Throwable
*/
@Around("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logAround(ProceedingJoinPoint pjp) throws Throwable {
Logger.info("开始在Around中加入日志");
pjp.proceed(); //执行程序
Logger.info("结束Around");
}
1.3、导入aspect包,spring使用的是AspectJ这个包来实现AOP,所以需要导入这个包。
1.4、在这个类上面使用@Aspect来声明这个类是一个切面类
@Component("logAspect") //让这个切面类被Spring所管理
@Aspect //声明这个类是一个切面类
public class LogAspect {
1.5、在相应的要进行操作横切点上面加入PointCut的说明
/**
* execution(* org.pm.spring.dao.*.add*(..))
* 第一个*表示任意返回值
* 第二个*表示org.pm.spring.dao包中的所有类
* 第三个*表示以add开头的所有方法
* (..)表示任意参数
* 可以使用||来加入多个条件
*/
@Before("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logStart(JoinPoint jp) {
//得到执行的对象
System.out.println(jp.getTarget());
//得到执行的方法
System.out.println(jp.getSignature().getName());
Logger.info("加入日志");
}
1.6、如果希望获取相应的调用信息,可以通过JoinPoint这个参数进行传递
public void logStart(JoinPoint jp) {
//得到执行的对象
System.out.println(jp.getTarget());
//得到执行的方法
System.out.println(jp.getSignature().getName());
Logger.info("加入日志");
}
1.7、其他知识
/**
* 函数调用完成之后执行
* @param jp
*/
@After("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logEnd(JoinPoint jp) {
Logger.info("方法调用结束加入日志");
}
/**
* 函数调用中执行
* @param pjp 一定要传入ProduceJoinPoint,通过这个参数数据才能往下走
* @throws Throwable
*/
@Around("execution(* org.pm.spring.dao.*.add*(..))||"
+ "execution(* org.pm.spring.dao.*.delete*(..))||"
+ "execution(* org.pm.spring.dao.*.update*(..))")
public void logAround(ProceedingJoinPoint pjp) throws Throwable {
Logger.info("开始在Around中加入日志");
pjp.proceed(); //执行程序
Logger.info("结束Around");
}
2、基于xml的实现:
使用Annotation需要在大量的joinpoint上加入execution,感觉不是很方便,使用xml会相对简单一些。
2.1、创建一个需要实现动态代理的类:
@Component("logAspect") //让这个切面类被Spring所管理
public class LogAspect {
public void logStart(JoinPoint jp) {
//得到执行的对象
System.out.println(jp.getTarget());
//得到执行的方法
System.out.println(jp.getSignature().getName());
Logger.info("加入日志");
}
public void logEnd(JoinPoint jp) {
Logger.info("方法调用结束加入日志");
}
public void logAround(ProceedingJoinPoint pjp) throws Throwable {
Logger.info("开始在Around中加入日志");
pjp.proceed(); //执行程序
Logger.info("结束Around");
}
2.2、导入aspect包,配置xml文件:
<aop:config>
<!-- 定义切面,ref="logAspect"表示代理类中定义的切面对象的引用 -->
<aop:aspect id="myLogAspect" ref="logAspect">
<!-- 在哪些位置加入相应的Aspect -->
<aop:pointcut id="logPointCut" expression="execution(* org.pm.spring.dao.*.add*(..))||
execution(* org.pm.spring.dao.*.delete*(..))||
execution(* org.pm.spring.dao.*.update*(..))" />
<aop:before method="logStart" pointcut-ref="logPointCut"/>
<aop:after method="logEnd" pointcut-ref="logPointCut"/>
<aop:around method="logAround" pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
转载于:https://my.oschina.net/pmos/blog/785898
上一篇: C++实现员工工资管理系统
下一篇: Spring AOP 的实现原理