Spring ioc与aop的理解

Spring


IOC(控制反转,也叫反转)

概念:将创建对象的过程交给Spring容器去处理,学名叫:控制反转也对反转,

要理解IOC就要解决下边的问题

“谁控制谁?控制什么?为何是反转(有反转就有正转)?哪些方面反转了”

答:

谁控制谁?控制什么?,在传统的开发中,我们是通过在对象内部new进行创建对象,是程序主动去创建依赖对象,而IOC是有专门容器来创建这些对象,由IOC容器来控制对象的创建,“谁控制谁”,就是IOC容器控制对象,“控制什么”主要控制了外部资源的获取

为何是反转?哪些方面反转了?,传统应用程序是由我们自己在对象中主动控制去获取依赖对象,也就是正转,而反转就是容器来帮我们创建及注入依赖对象,为何是反转,因为由我们容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,哪些方面反转了?依赖对象反转了

操作就是在applicationContext.xml文件中,配置bean

<bean id="userDao" class="com.imooc.dao.userDao" />
//在Test测试中,通过下面方式调用bean,完成对象的创建
ApplicationContext ac=new ClassPathXmlApplication("applicationContext.xml");
//根据bean的id查找到对象
//默认用构造器创建实例对象
UserDao userdao1=(UserDao)ac.getBean("userDao");
//用静态工厂方法创建实例对象 
UserDao userdao2=(UserDao)ac.getBean("userDao2");


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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.1.xsd

">
<!--1、默认构造器创建-->
<bean id="userDao" class="com.imooc.dao.UserDao">
</bean>
<!--2、静态工厂方法创建-->
 <bean id="userDao1" class="com.imooc.dao.UserFactory" factory-method="getUser1" >
 </bean>   
<!--3、实例工厂方法创建-->
<bean id="userFactory" class="ioc.service.UserFactory"></bean>
  <bean id="userDao2" factory-bean="userFactory" factory-method="getUser2"></bean>
    
</beans>
//静态工厂方法
public class UserFactory {
	
	// 静态方法
	public static User getUser1() {
		return new User();
	}
 
}
//实例工厂方法
public class UserFactory {
	
	//普通方法
	public User getUser2() {
		return new User();
	}
}

DI 依赖注入(三种方式注入)

1、构造器注入
private UserDao userdao;
public UserDao(UserDao userdao){
	this.userdao=userdao;
}

在applicationContext.xml文件中配置

<!--将userDao注入到userService中-->
<beans>
	<bean id="userDao" class="com.imooc.dao.UserDao" />
	<bean id="userService" class="com.imooc.service.UserService">
	<constructor-arg>
		<ref bean="userDao"></ref>
	</constructor-arg>
	</bean>
</beans>
2、set方式注入

在UserService中注入UserDao

private UserDao udao;
public UserDao getUserDao() {
        return uDao;
    }

    public void setUserDao(UserDao userDao) {
        this.uDao = userDao;
    }

在applicationContext.xml中配置

<bean id="userDao" class="com.imooc.dao.UserDao" />
<!--将userDao注入到userService中-->
<bean id="userService" class="com.imooc.service">
	<property name="udao" ref="userDao" />
</bean>
3、命名空间注入

为了简化setter方法,之前通过进行注入,p命名使用属性,<bean id="" class="" p:属性名=“普通值” p:属性名-ref=“引用值”>。不过这种方法在企业级开发中用的比较少。

如图所示,在实现p命名空间之前,要做一点事情。前面p必选,后面p可以改成别的名字。

[外链图片转存失败(img-nBKTaoLG-1568896907332)(F:\实训\前端\Center)]


AOP(面向切面编程)

那什么是面向切面编程呢?

答案是面向切面编程是纵向的的编程,比如我们要在程序A和程序B进行同样的日志记录,传统方法,我们需要在程序A和程序B中分别加入相关的日志记录操作,这样做,会让程序显得更加不够灵活,事务中杂着日志,让代码可读性低,那怎么解决呢?就用AOP只写一遍代码,程序A和程序B共用这一段代码不改动原代码。

在实际开发中,比如查询,删除,都需要记录日志、异常处理的操作,AOP把所有共同的代码都剥离出来,单独放在某个类中进行集中管理,在具体运行中,由容器进行动态织入这些代码。

AOP主要一般应用于签名验签,参数校检,日志记录,事务控制等。

Spring AOP的底层实现的两种方式:

一种是JDK动态代理,另一种是Cglib方式

JDK动态代理

有个限制:就是它只能为接口代理实例,而对于接口没有实现类的,就不起作用了

Cglib方式

可以为一个类创建一个创建一个子类,在子类中采用方法拦截的技术,拦截所有父类方法的调用并编写自己的拦截事务处理

两者区别:

jDK动态代理是面向接口的

Cgilib动态代理是通过字节码底层继承要代理的类来实现的

那Spring是采用那一种方式进行AOP事务处理的呢?
  1. 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作
  2. 如果要被代理的对象不是个实现类(比如是个接口),那么Spring会强制使用Cglib来实现动态代理

教科书般的理论讲得差不多了,下边来看看这两种方式的实现吧

JDK动态代理

[外链图片转存失败(img-HqP8MOMy-1568896907333)(F:\实训\前端\1568865687634.png)]

UserDaoImpl类实现了UserDao接口,需要对

UserDaoImpl目标类进行动态代理,简单说就是要有实现类,不然无效

public class MyJDKProxy {
    /**
     * 采用JDK的动态代理来实现对一个类的增加
     * 只能针对实现接口的类进行增强,如果目标类没有实现接口则无效
     */
    private Object obj;
    public MyJDKProxy(Object object){
        this.obj=object;
    }

    /**
     * newProxyInstance(用哪个类加载器去加载代理对象,动态代理类需要实现的接口,动态代理方法在执行时,
     * 会调用h里面的invoke方法去执行);
     * @return
     */
    public Object greateProxy(){
        Object proxy= Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if("findAll".equals(method.getName())){
                    System.out.println("进行日志记录!!");
                    return method.invoke(obj,args);
                }
                return method.invoke(obj,args);
            }
        });
        return proxy;
    }
}
CGlib方式

这种方式是为目标类创建一个子类,进行代理

public Object createProxy(){
// 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(obj.class);
 enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if("findAll".equals(method.getName())){
                    System.out.println("cglib记录日志!!!");
                    return methodProxy.invokeSuper(proxy,objects);
                }
                return methodProxy.invokeSuper(proxy,objects);
            }
        });
        Object proxy=enhancer.create();
        return proxy;
}

AOP涉及名词

  • 切面(aspect):当使用@Aspect注解修饰的类,就能被AOP容器识别为切面

    @Aspect
    @Component("myLog")
    
  • 通知(Advice):切面的具体实现,就是要给目标对织入的情情,以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before),后置通知(After)、异常通知(AfterThrowing)、后置、后置通知增强(AfterReturning)、环线通知(Around)

  • 切入点(Pointcut):用于定义通知应该切入到哪些点上,这种精确的匹配是由节入点的正则表达式来定义的

    //@Pointcut声明一个切入点,范围为com.mooc.service.impl包下的findAll()方法
    @Pointcut("execution(* com.mooc.service.impl.*.findAll(..))")
        public void pointCut(){
            System.out.println("切面");
        }
        @Before("pointCut()")//前置通知
        public void before(JoinPoint joinPoint){
            System.out.println("前置通知!!");
        }
        @After("pointCut()")//后置通知
        public void after(JoinPoint joinPoint){
            System.out.println("后置通知!!");
        }
        @AfterReturning(value = "pointCut()",returning ="obj")
        public void afterruning(Object obj){
            System.out.println("后置增强"+obj);
        }
        @AfterThrowing(value = "pointCut()",throwing = "e")
        public void afterThrowing(Throwable e){
            System.out.println("异常"+e.getMessage());
        }
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            System.out.println("前置环绕");
            Object obj=point.proceed();
            System.out.println("后置环绕");
            return obj;
        }
    
    • 目标对象(Target):那些即将切入切面的对象,就比如上表中的com.mooc.service.impl包下的UserServiceImpl类对象,等待切入
    • 代理对象(Proxy):可以简单理解为为目标对象加上共有的功能,比如对目标类进行日志检查,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值