Spring存在意义:
Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志,权限验证,事务等功能时,只能在在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。
1.代理模式:
代理模式:为其他对象提供一种代理以控制对这个对象的访问。即A要做一件事,在没有代理的时候需要自己动手,当有了代理B以后就可以让B去替他完成;
2.静态代理:
public interface IUserDAO {
public void run();
}
public class UserDAO implements IUserDAO {
@Override
public void run() {
System.out.println("事务运行");
}
}
public class Proxy implements IUserDAO {
private IUserDAO target=new UserDAO();
@Override
public void run() {
System.out.println("事务运行前");
target.run();
System.out.println("事务运行后");
}
}
public class Test {
public static void main(String[] args) {
IUserDAO proxy=new Proxy();
proxy.run();
}
}
静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本,为了减少成本就需要使用动态代理。
3.动态代理:
动态代理模式:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。
public interface IUserDAO {
public void save();
public void find();
}
class RealUser implements IUserDAO{
@Override
public void save() {
System.out.println("模拟:保存用户");
}
@Override
public void find() {
System.out.println("查询");
}
}
class ProxyFactory{
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
public Object getProxyInstance(){
Object proxy=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目标对象使用的类加载
target.getClass().getInterfaces(),//目标对象实现的接口
//执行代理对象方法是触发,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行方法名
String name=method.getName();
//返回值
Object result=null;
if("find".equals(name)){
result=method.invoke(target,args);
}else {
System.out.println("开始事务");
result=method.invoke(target,args);
System.out.println("事务提交");
}return result;
}
}
);return proxy;
}
}
class Client{
public static void main(String[] args) {
IUserDAO target=new RealUser();
System.out.println("目标对象:"+target.getClass());
IUserDAO proxy=(IUserDAO) new ProxyFactory(target).getProxyInstance();
System.out.println("代理对象:"+proxy.getClass());
proxy.save();
}
}
JDK代理:目标类要有实现的接口;
CGLIB代理:目标类没有实现接口,其是以动态生成目标类的子类来实现的,目标类不能被final修饰(不能继承);
4.SpringAOP原理:
SpringAOP基本理论:
- 面向切面编程:
DI有助于对象之间的解耦;
AOP实现横切关注点与其所影响的对象之间的解耦;(散布与应用中多处的功能为横切关注点)
- 通知(Advice):
定义了切面是什么以及何处使用,即做什么和什么时候做;
- 连接点(JoinPoint):
spring允许使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点;
- 切入点(PointCut):
在连接点的基础上,来定义切入点,一个类里,有许多方法,就有许多个连接点,但是你并不想在所有方法附近都使用通知(使用叫织入),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法;
"execution(* com.answer.springAOP2.IUserDAO.save())"
- 切面(Aspect):
即通知和切入点的结合,通知是做什么和什么时候做,切入点是在哪做;
- 引入(Introduction):
允许我们向现有的类添加新方法属性,就是把切面(也就是新方法属性:通知定义的)用到目标类中;
- 织入(Weaving):
把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。
关键就是:切点定义了哪些连接点会得到通知;
- 编译期:切面在目标类编译时被织入,需要特殊的编译器;
- 类加载期:切面在目标类加载到JVM时织入,需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前加强该目标类的字节码;
- 运行期:切面在运行的某个时刻被织入,AOP为目标类动态创建代理对象;
A.SpringAOP是动态代理模式;
B.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象;
C.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法;
SpringAOP(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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="cglib" class="com.answer.springAOP1.Cglib"></bean>
<bean id="jdk" class="com.answer.springAOP1.Jdk"></bean>
<bean id="transcantionaop" class="com.answer.springAOP1.TranscationAOP"></bean>
<aop:config>
<!-- 切入点表达式定义-->
<aop:pointcut id="transcationPointcut" expression="execution(* com.answer.springAOP1.*.*(..))"></aop:pointcut>
<aop:aspect ref="transcantionaop">
<aop:before method="beforeTransaction" pointcut-ref="transcationPointcut"></aop:before>
<aop:after method="afterTranscation" pointcut-ref="transcationPointcut"></aop:after>
<aop:after-returning method="afterReturn" pointcut-ref="transcationPointcut" returning="result"></aop:after-returning>
<aop:around method="round" pointcut-ref="transcationPointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
public interface IUserDAO {
public String save();
}
class Cglib{
public String save(){
System.out.println("Cglib代理");
return "不需要接口实现";
}
}
class Jdk implements IUserDAO{
@Override
public String save() {
System.out.println("JDK代理");
return "需要一个接口实现";
}
}
class TranscationAOP{
public void beforeTransaction(){
System.out.println("前置增强");
}
public void afterTranscation(){
System.out.println("后置增强");
}
public void afterReturn(Object result){
System.out.println("后置返回增强"+result);
}
public void afterThrow(){
System.out.println("后置异常增强");
}
public Object round(ProceedingJoinPoint pjp) throws Throwable {
Object result=null;
System.out.println("环绕前:");
result=pjp.proceed();
System.out.println("环绕后:");
return result;
}
}
class Test{
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("aop1.xml");
IUserDAO userDAO=(IUserDAO) applicationContext.getBean("jdk");
//System.out.println(userDAO.getClass());
userDAO.save();
Cglib cglib=(Cglib) applicationContext.getBean("cglib");
cglib.save();
}
}
SpringAOP(注解):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.answer.springAOP2">
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
public interface IUserDAO {
public String save();
public void mine();
}
@Component("jdk")
public class Jdk implements IUserDAO {
@Override
public String save() {
System.out.println("JDK动态代理");
return "需要接口";
}
@Override
public void mine() {
System.out.println("我不需要切面");
}
}
@Component("cglib")
public class Cglib {
public String save(){
System.out.println("CGLIB动态代理");
return "不需要接口";
}
}
@Aspect
public class MyAspect {
@Before("execution(* com.answer.springAOP2.IUserDAO.save())")
public void beforeTransaction(){
System.out.println("前置增强");
}
@After("execution(* com.answer.springAOP2.IUserDAO.save())")
public void afterTranscation(){
System.out.println("后置增强");
}
@AfterReturning(value = "execution(* com.answer.springAOP2.IUserDAO.save())",returning = "result")
public void afterReturn(Object result){
System.out.println("后置返回增强"+result);
}
@AfterThrowing("execution(* com.answer.springAOP2.IUserDAO.save())")
public void afterThrow(){
System.out.println("后置异常增强");
}
@Around("execution(* com.answer.springAOP2.IUserDAO.save())")
public Object round(ProceedingJoinPoint pjp) throws Throwable {
Object result=null;
System.out.println("环绕前:");
result=pjp.proceed();
System.out.println("环绕后:");
return result;
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("aop2.xml");
IUserDAO iUserDAO=(IUserDAO) applicationContext.getBean("jdk");
iUserDAO.save();
iUserDAO.mine();
//Cglib cglib=(Cglib) applicationContext.getBean("cglib");
//cglib.save();
}
}
SpringAOP用处:
1.Spring声明式事务管理配置。
2.Controller层的参数校验。
3.使用Spring AOP实现MySQL数据库读写分离案例分析
4.在执行方法前,判断是否具有权限。
5.对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。
6.信息过滤,页面转发等;