1.Spring AOP的通知advice
01.接口代碼:
package cn.pb.dao;
public interface UserDao {
//主業務
String add();
//主業務
void del();
}
02.實現類代碼:
package cn.pb.dao;
public class UserDaoImpl implements UserDao{
//主業務
public String add() {
//模擬異常
//int a=8/0;
System.out.println("add ok!");
return "新增成功!";
}
//主業務
public void del() {
System.out.println("del ok!");
}
03.增強通知類:
001.前置增強類:
package cn.pb.advices;
/**
* 前置增強類 實現MethodBeforeAdvice接口
* 在目標對象方法執行之前執行
*/
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeAdvice implements MethodBeforeAdvice {
/**
*
* @param method :目標方法
* @param args :目標方法參數
* @param target :目標對象
* @throws Throwable
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置增強====================》");
}
}
002.后置增強類:
package cn.pb.advices;
/**
* 后置增強類 實現AfterReturningAdvice接口
* 在目標對象方法執行之后執行
*/
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterAdvice implements AfterReturningAdvice {
/**
*
* @param target:目標對象
* @param method :目標方法
* @param args :目標方法參數
* @param returnValue :目標方法返回值
* @throws Throwable
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置增強==================》");
}
}
003.環繞增強類:
package cn.pb.advices;
/**
* 環繞增強 :
* 在前置通知 之后,
* 后置通知之前執行環繞通知!
*/
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundAdvice implements MethodInterceptor {
/**
* 在前置通知 之后,后置通知之前執行環繞通知!
* 可以獲取方法的返回值,並且改變!
* @param methodInvocation 方法的執行器, getMethod 包含了方法中的所有方法
* @return
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("執行方法之前的 環繞通知");
//執行目標方法
Object result= methodInvocation.proceed();
if (result!=null){
//對方法的返回值進行修改
result="xiaoheihei";
}
System.out.println("執行方法之后的 環繞通知");
return result;
}
}
004.異常增強類:
package cn.pb.advices;
/**
* 異常增強類:在目標方法出現異常的時候執行
* 實現ThrowsAdvice接口
*/
import org.springframework.aop.ThrowsAdvice;
public class ExceptionAdvice implements ThrowsAdvice{
public void throwsAdvice(){
System.out.println("方法在執行過程中出現了異常!");
}
}
04.applicationContext.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--01.配置目標對象-->
<bean id="userDao" class="cn.pb.dao.UserDaoImpl"/>
<!--02.配置通知-->
<bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>
<bean id="afterAdvice" class="cn.pb.advices.AfterAdvice"/>
<bean id="aroundAdvice" class="cn.pb.advices.AroundAdvice"/>
<!--03.通過配置代理工廠bean,生成代理類,來把通知織入到目標對象
問題:只能管理 通知!
01.只能將切面織入到目標類的所有方法中!
02.只能配置一個 目標對象
-->
<bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--注冊目標對象 如果nam為target 那么ref="userDao" -->
<property name="targetName" value="userDao"/>
<!--注冊通知-->
<property name="interceptorNames" value="beforeAdvice,afterAdvice,aroundAdvice"/>
</bean>
<!--配置異常目標對象-->
<bean id="userException" class="cn.pb.exceptionPackage.UserServiceImpl"/>
<!--配置異常通知-->
<bean id="myException" class="cn.pb.advices.ExceptionAdvice"/>
<!--現在是一個service對應一個ProxyFactoryBean 這樣不可以!-->
<bean id="exceptionProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--注冊目標對象 -->
<property name="targetName" value="userException"/>
<!--注冊通知-->
<property name="interceptorNames">
<array>
<value>myException</value> <!--異常通知-->
</array>
</property>
<!--代理類的優化 設置之后程序就會自動選擇是使用JDK動態代理還是使用cglib動態代理-->
<property name="optimize" value="true"/>
<!-- <property name="proxyTargetClass" value="true"/>
proxyTargetClass:默認是false ,默認執行jdk動態代理!
設置成true,強制執行cglib!
optimize : 代理類的優化
有接口就是用jdk,沒有接口使用cglib動態代理-->
</bean>
<!--
我們的動態代理 (在程序運行期間,動態生成的代理類) 分為兩種方式:
01.jdk 只能應用於實現接口的情況
02.cglib 應用於實現接口和類的情況
如果我們是接口的情況,使用jdk效率高!
如果我們是類的情況,必須使用cglib!
問題?
程序 spring容器怎么知道我們是用的類還是接口??
public class ProxyConfig implements Serializable
private boolean proxyTargetClass = false;
private boolean optimize = false;
spring底層默認使用cglib! 現在我們的項目中使用的是接口!
用spring默認的性能不高!
proxyTargetClass 和optimize都是用來設置 我們使用的代理模式是jdk還是cglib!
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
根據我們配置文件中 proxyTargetClass 和 optimize的配置
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
根據目標對象返回對應的動態代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
-->
</beans>
05.測試代碼:
package cn.pb;
import cn.pb.dao.UserDao;
import cn.pb.exceptionPackage.ServiceException;
import cn.pb.exceptionPackage.UserException;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAdvice {
/**
* 前置 后置 通知測試
*/
@Test
public void testBefore(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao service= (UserDao) context.getBean("userProxy");
service.add();
System.out.println("*************");
service.del();
}
/**
* 環繞 通知測試
*/
@Test
public void testAround(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao service= (UserDao) context.getBean("userProxy");
String result= service.add();
System.out.println(result);
System.out.println("*************");
service.del();
}
/**
* 異常通知測試
*/
@Test
public void testException(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
ServiceException service= (ServiceException) context.getBean("exceptionProxy");
try {
service.chechUser("admins",25);
} catch (UserException e) {
e.printStackTrace();
}
}
}
2.Spring AOP的顧問advisor
01.readMe
顧問:在通知的基礎之上,在細化我們的aop切面!
通知和顧問都是切面的實現方式!
通知是顧問的一個屬性!
顧問會通過我們的設置,將不同的通知,在不通過的時間點,把切面
織入到不同的切入點!
PointCutAdvisor接口!
比較常用的兩個實現類:
NameMatchMethodPointcutAdvisor :根據切入點(主業務方法)名稱織入切面!
RegexpMethodPointcutAdvisor :根據自定義的正則表達式織入切面!
正則表達式中常用的三個運算符
. 任意單個字符
+ 表示字符出現一次或者多次
* 表示字符出現0次或者多次
02.接口代碼:
package cn.pb.dao;
public interface UserDao {
//主業務
void add();
//主業務
void del();
}
03.實現類代碼:
package cn.pb.dao.impl;
import cn.pb.dao.UserDao;
public class UserDaoImpl implements UserDao{
//主業務
public void add() {
System.out.println("add ok!");
}
//主業務
public void del() {
System.out.println("del ok!");
}
}
04.增強類代碼:
package cn.pb.advices;
/**
* 前置增強類 在目標方法執行之前 執行
* 要實現MethodBeforeAdvice接口
*/
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeAdvice implements MethodBeforeAdvice {
/**
* 在目標方法執行之前
* @param method 目標方法
* @param args 目標方法的參數列表
* @param target 目標對象
* @throws Throwable
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置增強==============》");
}
}
05.applicationContext.xml文件:
001.NameMatchMethodPointcutAdvisor :根據切入點(主業務方法)名稱織入切面!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--01.配置目標對象-->
<bean id="userDao" class="cn.pb.dao.impl.UserDaoImpl"/>
<!--02.配置增強 通知-->
<bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>
<!---配置顧問 實現了 在指定的主業務方法中 增強-->
<bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<!--通知就是顧問中的一個屬性-->
<property name="advice" ref="beforeAdvice"/>
<!--配置切入點 這里的切入點指的是 方法的簡寫!-->
<property name="mappedNames" value="add,del"/>
</bean>
<!--03.配置代理對象-->
<bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--注冊目標對象-->
<property name="target" ref="userDao"/>
<!--注冊顧問-->
<property name="interceptorNames" value="myAdvisor"/>
</bean>
</beans>
002.RegexpMethodPointcutAdvisor :根據自定義的正則表達式織入切面!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--01.配置目標對象-->
<bean id="userDao" class="cn.pb.dao.impl.UserDaoImpl"/>
<!--02.配置增強 通知-->
<bean id="beforeAdvice" class="cn.pb.advices.BeforeAdvice"/>
<!---配置顧問 實現了 在指定的主業務方法中 增強-->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--通知就是顧問中的一個屬性-->
<property name="advice" ref="beforeAdvice"/>
<!--配置切入點 這里的切入點指的是 方法的全限定方法名
cn.pb.dao.impl.UserServiceImpl.add
cn.pb.dao.impl.UserServiceImpl.del-->
<!-- <property name="pattern" value=".*add.*"/> 匹配單個方法-->
<!-- <property name="pattern" value=".*mpl.*"/>匹配多個方法-->
<!--<property name="patterns" value=".*add.*,.*del.*"/> 匹配多個方法-->
<property name="pattern" value=".*add.*|.*del.*"/>
</bean>
<!--03.配置代理對象-->
<bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--注冊目標對象-->
<property name="target" ref="userDao"/>
<!--注冊顧問-->
<property name="interceptorNames" value="myAdvisor"/>
</bean>
</beans>
06.測試代碼:
package cn.pb;
import cn.pb.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
/**
* 測試NameMatchMethodPointcutAdvisor
*/
@Test
public void testNameMethod(){
ApplicationContext context=new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao proxy= (UserDao) context.getBean("userProxy");
proxy.add();
System.out.println("***************");
proxy.del();
}
/**
* 測試RegexpMethodPointcutAdvisor
*/
@Test
public void testRegexpMethod(){
ApplicationContext context=new
ClassPathXmlApplicationContext("regexp.xml");
UserDao proxy= (UserDao) context.getBean("userProxy");
proxy.add();
System.out.println("***************");
proxy.del();
}
}