Spring AOP

原创
2016/11/10 21:58
阅读数 182

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>

 

展开阅读全文
打赏
0
8 收藏
分享
加载中
更多评论
打赏
0 评论
8 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部