AOP中关键性概念
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
注1:完成具体的业务逻辑
通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
注2:完成切面编程
代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),
例子:外科医生+护士
注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)
如何实现AOP
目标对象只负责业务逻辑代码
通知对象负责AOP代码,这二个对象都没有AOP的功能,只有代理对象才有
接下来贴上案例代码来实现通知:
biz
public interface IBookBiz {
// 购书
public boolean buy(String userName, String bookName, Double price);
// 发表书评
public String comment(String userName, String comments);
}
biz实现
public class BookBizImpl implements IBookBiz {
public BookBizImpl() {
super();
}
public boolean buy(String userName, String bookName, Double price) {
// 通过控制台的输出方式模拟购书 如果价格不对将会触发异常,导致程序停止,但是触发前的事件依然会执行
if (null == price || price <= 0) {
throw new PriceException("book price exception");
}
System.out.println(userName + " buy " + bookName + ", spend ASD " + price);
return true;
}
public String comment(String userName, String comments) {
// 通过控制台的输出方式模拟发表书评
System.out.println(userName + " say:" + comments);
return "123";
}
}
然后我们写一个前后置通知的类
/**
* @program: Maven
* @description: 前置后置通知
* @author: hw
* MethodBeforeAdvice 为前置通知的接口 AfterReturningAdvice为后置通知接口
**/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
/**
* @Description: 前置通知方法
* @Param: [method, objects, o]
* @return: void
* @Author: hw
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
//类名
String name = o.getClass().getName();
//方法名
String name1 = method.getName();
//参数
String s = Arrays.toString(objects);
System.out.println("前置类名" + name + "方法名" + name1 + "参数" + s);
}
/**
* @Description: 后置通知方法
* @Param: [o, method, objects, o1]
* @return: void
* @Author: hw
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
//类名
String name = o1.getClass().getName();
//方法名
String name1 = method.getName();
//参数
String s = Arrays.toString(objects);
System.out.println("后置类名" + name + "方法名" + name1 + "参数" + s+"返回值"+o);
}
}
这是环绕通知的类,也就是即会在方法执行前执行也会在方法执行后执行,概念像struts的拦截器
/**
* @program: Maven
* @description: 环绕通知
* @author: hw
**/
public class MyMethodInterceptor implements MethodInterceptor {
/**
* @Description: 环绕通知, 前后都执行
* @Param: [methodInvocation]
* @return: java.lang.Object
* @Author: hw
* @Date: 2018/12/5
*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//执行前
//类名
String name = methodInvocation.getThis().getClass().getName();
//方法名
String name1 = methodInvocation.getMethod().getName();
//参数
String s = Arrays.toString(methodInvocation.getArguments());
System.out.println("环绕前"+ name + "方法名" + name1 + "参数" + s);
//执行后通知以下面这行代码分割
Object proceed = methodInvocation.proceed();//返回值
//后置通知
System.out.println("环绕后" + name + "方法名" + name1 + "参数" + s+"返回值"+proceed);
return proceed;
}
}
异常类
/**
* @program: Maven
* @description: 异常
* @author: hw
**/
public class MyThrowsAdvice implements ThrowsAdvice {
/** @Description: 这个方法需要手写出来,并且方法名必须是这个,但是可以通过方法重构使用多个方法
* @Param: [ex]
* @return: void
* @Author: hw
*/
public void afterThrowing(PriceException ex) {
System.out.println("价格异常");
}
}
/**
* @Description: 异常类
* @Param:
* @return:
* @Author: hw
*/
public class PriceException extends RuntimeException {
public PriceException() {
super();
}
public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public PriceException(String message, Throwable cause) {
super(message, cause);
}
public PriceException(String message) {
super(message);
}
public PriceException(Throwable cause) {
super(cause);
}
}
接下来是我们最后的配置文件
<!--aop-->
<!--目标-->
<bean class="com.hw.two.biz.impl.BookBizImpl" name="bookBiz"></bean>
<!--前置通知 和后置通知-->
<bean class="com.hw.two.advice.MyMethodBeforeAdvice" name="MymethodBeforeAdvice"></bean>
<!--环绕通知-->
<bean class="com.hw.two.advice.MyMethodInterceptor" name="myinterceptor"></bean>
<!--异常-->
<bean class="com.hw.two.advice.MyThrowsAdvice" name="throwsAdvice"></bean>
<!--过滤通知-->
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
name="pointcutAdvisor">
<!--配置需要过来的通知-->
<property name="advice" ref="MymethodBeforeAdvice"></property>
<!--匹配该正则,符合的时候才使用通知-->
<property name="pattern" value=".*buy"></property>
</bean>
<!--代理工厂-->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" name="bookProxy">
<!--设置目标-->
<property name="target" ref="bookBiz"></property>
<!--配置接口 填入目标实现的接口 因为接口可以多实现,所以标签为list-->
<property name="proxyInterfaces">
<list>
<value>com.hw.two.biz.IBookBiz</value>
</list>
</property>
<!--目标通知-->
<property name="interceptorNames">
<list>
<!--前后通知-->
<!--<value>MymethodBeforeAdvice</value>-->
<!--过滤通知-->
<value>pointcutAdvisor</value>
<!--环绕通知-->
<value>myinterceptor</value>
<!--异常-->
<value>throwsAdvice</value>
</list>
</property>
</bean>
测试类
/**
* @program: Maven
* @description: 通知测试
* @author: hw
**/
public class AdviceTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
Object bookProxy = applicationContext.getBean("bookProxy");
IBookBiz biz= (IBookBiz) bookProxy;
biz.buy("猴子","升序",12.5);
System.out.println();
biz.comment("猴子","sss");
}
}