AOP:Aspect-Oriented-Programming,面向切面编程。
public class UserDAOImpl3 implements UserDAO {
private UserDAO userDAO = new UserDAOImpl();
@Override
public void save(User u) {
System.out.println("save start...");
userDAO.save(u);
System.out.println("save end...");
}
}
上面的成员变量是父类的引用指向子类的对象,可以通过setUserDao方法进行动态绑定具体实现类。
3. 对于大量的bean都需要加某个业务逻辑时,通过以上2个方法,难以实现,且造成代码的大量重复。此时我们需要一个新的方法,那就是AOP,面向切向编程。
AOP好处:可以动态的添加和删除在切面上的逻辑而不影响原来的执行代码。
动态代理实现AOP。
1. 编写业务逻辑类
public class LogInterceptor implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public void beforeMethod(Method m) {
System.out.println(m.getName() + " start...");
}
@Override
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
beforeMethod(m);
m.invoke(target, args);
return null;
}
}
该业务逻辑类实现了InvocationHandler 接口,重写了invoke方法,在invoke方法中调用beforeMethod()方法的,然后调用被代理对象的方法m。
2. 测试代码
@Test
public void testProxy() {
UserDAO userDAO = new UserDAOImpl();
LogInterceptor li = new LogInterceptor();
li.setTarget(userDAO);
UserDAO userDAOProxy = (UserDAO) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),
new Class[] { UserDAO.class }, li);
userDAOProxy.save(new User());
userDAOProxy.delete();
}
(1)实例化业务逻辑类li。
(2)设置被代理对象target。
(3)调用Proxy.newProxyInstance方法生成代理对象userDAOProxy 。参数为:类加载器,实现接口,负责业务处理的类。
(4)代理对象调用相应方法即可。
在beans.xml中增加AOP的配置信息。
(1)xmlns:aop="http://www.springframework.org/schema/aop"
(2)http://www.springframework.org/schema/aop
(3)http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
(4)子标签下增加<aop:aspectj-autoproxy/>
使用Aspectj的方式定义AOP。
AspectJ是一个面向切面的框架,它扩展了JAVA语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
表明这是一个切面。
例子如下:
@Aspect
@Component("logInterceptor")
public class LogInterceptor {
@Before("execution(public * com.shen.dao.impl.UserDAOImpl.save(com.shen.model.User))")
public void befor() {
System.out.println("method start...");
}
}
JoinPoint:织入点。
PointCut:织入点的集合,且名字等于下面的方法名字。
@Pointcut("execution(public * com.shen.dao..*.*(..))")
public void myMethod() {};
@Before("myMethod()")
public void before() {……}
Pointcut的名字就是myMethod()。
- Aspect:切面。
Advice:在织入点上的建议。比如:@Before、@After
Target:被代理对象,织入到哪里
Weave:织入
@Beforce(切入点语法|全类名.方法())
在织入点函数执行前,会执行被这个建议注解的方法。
@AfterReturning()
织入点包含的函数正常执行完,会执行被这个建议注解的方法。
@AfterThrowing()
织入点包含的函数抛出异常,会执行被这个建议注解的方法。
@After()
当织入点函数正常返回或抛出异常时,会执行被这个建议注解的方法。
@Around()
前面也可以加逻辑,后面也可以加逻辑。注意该方法的第一个参数必须是ProceedingJoinPoint类型的,返回值在不确定返回类型情况下,用Object。
@Around("com.shen.dao.impl.UserDAOImpl.addUser(com.shen.dao.UserDAO)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
//前逻辑
Object retVal=pjp.proceed();//方法继续运行
//后逻辑
return retVal;
}
基本上同Annotation的原理。
1. 先编写用到的业务逻辑类,比如生成日志的功能类,包含beforeLog()、afterLog(),比如该类为LogCreateor。
2. 配置XML,先给出代码。
<bean id="logInterceptor" class="com.shen.aop.LogCreateor"></bean>
<aop:config>
<!-- <aop:pointcut expression="execution(public * com.shen.service..*.add(..))"
id="servicePointcut" />
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut-ref="servicePointcut" />
</aop:aspect> -->
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut="execution(public * com.shen.service..*.add(..))" />
</aop:aspect>
</aop:config>
(1)声明LogCreateor为一个bean,让Spring自动为其初始化。
(2)使用<aop:config>
子标签声明AOP的配置。
(3)在外部使用<aop:pointcut>
子标签声明一个全局的织入点,给出织入点语法和织入点id。
(4)使用<aop:aspect>
子标签声明一个切面,并给出该切面的bean的ID。PS:在该标签内也可声明织入点,该织入点只在该切面管用。
(5)在aspect标签中使用<aop:before(各种advice)>
子标签声明一个建议,要给出切面中使用的方法名,以及要在哪个织入点ID使用该方法。也可以在该标签中的属性pointcut="织入点语法"
直接定义织入点,而不是用局部或者全局的织入点ID。
很多情况下,使用别人的切面类逻辑不能使用Annotation(因为没有源码),但此时可以使用XML。故XML更常用。