Spring 两大特性IOC和AOP

IOC和AOP也需要个容器承载,spring相当这个容器,如果要用到这两个特性要在spring基础上去运行。

IOC

在传统的程序开发中,一个类如果需要用到另外一个类的功能,常常采用组合(也就是new 对象)或者使用工厂方法(工厂方法最终也是需要new) 的方式。这样类与类之间的耦合度变高了。

IOC(Inversion of Control)控制反转,DI(Dependency Injection)依赖注入,其实两者本质上是没有区别的

例如:在A类中调用B类的方法,那么我们就称 A依赖B,B为被依赖(对象),相信这点大家能够理解。

在spring中,B的实例对象被看成Bean对象,这个Bean对象由spring容器进行创建和管理,如此一来,A获取B的实例对象就不是由自己主动去获取,而是被动接受spring给它设值,那么,这个主动变为被动,就可以理解为“控制反转”

而另一种说法,从spring容器的角度上看,它负责把A的依赖对象B(B是被依赖对象)注入给了A,所以我们可以理解为“依赖注入”

达到的效果:

你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。

依赖注入的两种方式:

set方法注入和有参构造注入

第一种使用set方法注入
public class User{
private String name;
public void setName(String name){
    this.name = name;
    }

}

User user= new User();
user.setName("abc");

使用set方法注入属性
<bean id="book" class= "cn.itcast.property.Book">
    <!--注入属性值
     name属性值,类里面定义的属性名称
     value属性,设置具体的值
    -->
    <property name ="bookname" value="深入理解java虚拟机"></property>
    
</bean>

第二种有参数构造注入
public class User{
    private String name;
    public User(String name ){
        this.name = name;
}

User user = new User("lucy");

使用有参数构造注入属性
<bean id ="demo" class="cn.itcast.property.PropertyDemo1">
    <constructor-ary name ="username" value="测试"></constructor-ary>
</bean>

IOC和DI的优点
降低了组件间的耦合度,增强了可维护性和复用性

 

IOC的底层原理
工厂模式+反射  可以采用dom4j来解析xml配置文件

我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

 

 

 

Spring通过控制反转(IOC)的技术促进了低耦合,一个对象依赖的其他对象会被通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。控制反转(Inversion of Control),是一种设计思想,一个重要的面向对象编程的法则,它能知道我们设计出松耦合的程序。传动应用程序都是由我们在类内主动创建依赖对象,从而导致类与类之间高耦合,难于测试,有了IOC容器之后,把创建和查找依赖对象的控制权,交个类容器,由容器进行注入组合对象,所以对象与对象之间就是松散耦合的,这样便于测试,功能复用,使整体程序体系结构变的灵活。DI(依赖注入)是实现Ioc的一种方法,控制反转是,获得依赖对象的方式反转了,其实Ioc对变成带来的最大改变不是从代码,是思想上,发生了“主从换位”的变化。应用程序原本是老大,要获得什么资源都是主动出击,但是Ioc/DI思想中,应用程序就变成被动的了,被动的等待Ioc容器来创建并注入所需要的资源了。

一、Bean的定义,<beans../>元素是spring配置文件的根元素,<beans.../>元素可以包含多个<bean..../>子元素。bean可以理解为实例化的对象,每一个Bean对应Spring容器里的一个Java实例,所以定义一个Bean需要有两个属性

Id,确定该Bean的唯一标识符,容器对Bean管理、访问、以及该Bean的依赖关系,都通过该属性完成,Bean的id属性在Spring容器中是唯一的。

Class,指定new的哪一个类,不能是接口类,spring会直接使用new关键字创建改Bean的实例,因此,这里必须提供Bean实现类的类名。例如,在配置一个Bean时,该Bean实现类中必须有个一个无参构造器,可以没有构造方法,如果有构造方法了,必须有一个无参数的构造方法

<bean id="bookDao" class="com.service.impl.BookDao"/>

容器的启动,暂时没用用tomcat容器,所以用spring自带的容器启动它,默认去resources

 ApplicationContext context = new ClassPathXmlApplicationContext("IOCBeans01.xml");

通过xml注入的两种方式

一个是注解扫包

<bean id="bookDao" class="com.service.impl.BookDao"/>
    <context:component-scan base-package="com.service.impl" />

把bookDao这个bean 赋值给@Autowired注解下面的那个成员变量

一个是xml配置

 <bean id="bookService" class="com.service.impl.BookService">
        <property name="iBookDao" ref="bookDao"></property>
    </bean>

把bookDao这个bean,赋值给iBookDao这个成员变量

二、参数传递

constructor-arg 代表构造方法 和值

property name 代表set方法

 <bean id="userDao" class="com.service.impl.UserDao">
        <constructor-arg name="" value=""/>
        <property name="" value=""/>
    </bean>

三、Bean回调方法

回调方法,在初始化这个bean的时候,先执行一个方法

执行测试的时候,去启动容器,在启动容器过程中,就会出现new的这个操作,只针对这个bean自己初始化用

  <bean id="userService" class="com.service.impl.UserService"
        init-method="initMethod" destroy-method="destroyMethod">
        <property name="iUserDao" ref="userDao"/>
    </bean>
  public void initMethod(){
        System.out.println("初始化操作");
    }
    public void destroyMethod(){
        System.out.println("关闭方法");
    }
 UserService userService = context.getBean("userService",UserService.class);

这个getBean里的是xml里配置的id,如果没有提供名字,就用spring默认的命名机制,即类名的第一个字母小写,强制类型转换,getBean拿到的是一个object类型,需要做类型转化,有两种方式,第一种在bean的名字后面加上UserService.class, 第二种在前面(UserService) context.getBean("userService")

 

四、使用Spring注解配置IOC

首先要在xml中配置扫描包,使用注解情况下要加这个扫描,

 <context:component-scan base-package="com.service.impl" />

注解分为三类

@Repository用于DAO层的实现类进行注解

@Service用于业务层注解,功能和@Component

@Controller用于控制层注解,功能和@Component

五、初始化回调注解与销毁回调注解

 @PostConstruct
    public void initMethod(){
        System.out.println("构造方法之后执行,初始化");
    }
    @PreDestroy
    public void destroyMethod(){
        System.out.println("销毁之前执行");
    }

销毁是当bean被处理掉了之后,就会执行,跟执行什么方法没有关系

注解@PostConstruct等同于 xml里的 init-method="initMethod"

注解@PreDestroy 等同于xml里的destroy-method="destroyMethod"

 

六、自动装配/注入

使用注解@Autowired

 

七、零配置实现IOC

 

八、通过注解@Value获取properties配置

取properties里的值步骤

首先要有一个properties文件 ,类似一个map对象,key-value

然后新建一个包下面一个类,属性和properties里的一样 ,类用@Componet 组件的意思 扫描包, 添加get set  toString 然后用spring初始化 注解@PostConstruct,输出,

然后新建一个xml ,用扫描包和位置的标签 

component-scan base-package=   

资源点位,资源的位置

property-placeholder location=“config/config.properties”

创建一个测试类 

ApplicationContext applicationContext;

applicationContext = new ClassPathXmlApplicationContext(config.xml);

九、bean作用域

 

十、扫描过滤expression表达式

 

spring 注入bean的方式两种xml写法,一种是,相当对这个iBookDao成员变量赋值,把bookDao这个bean注入到bookService这个类里

 <bean id="bookDao" class="com.service.impl.BookDao"/> 
 <bean id="bookService" class="com..service.impl.BookService">
        <property name="iBookDao" ref="bookDao"></property>
    </bean>

一种是注解方式,扫描包,在对应的类里的成员变量加上注解@Autowired,扫描到这个成员变量。@Autowired是按照类型注入的

<bean id="bookDao" class="com.service.impl.BookDao"/>
    <context:component-scan base-package="com.service.impl" />

 

AOP

AOP(Aspect Oriented Programming)面向切面编程,什么地方应用,一般应用在日志。避免重复代码,较少对业务代码的入侵

AOP的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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


<context:component-scan base-package="com..service.impl"></context:component-scan>
    <!--当有切面被触发的时候去执行这类里面的方法,有了切面之后需要一个类(bean)去承载执行-->
    <bean id="halder" class="com.long.lesson.service.Halder"/>
    <aop:config>
        <!-- 切入点,在什么情况下去执行aop的内容-->
        <aop:aspect id="time" ref="halder">
            <!--切入点,在触发什么条件下进入切入点-->
            <!--切入点表达式主要是触发条件,含义是Service类里面的所有方法,并且需要带参数才会切入-->
            <!--id的值,和下面的pointcut-ref的值是一致的,说明before 和after执行的切点是id相同的那个
                 id和pointcut-ref 是一一对应的-->
            <aop:pointcut id="addAllMethod"
                          expression="execution(* com.long.lesson.service.impl.*Service.*(*))"/>
            <!--method的值是 切面类里的方法 -->
            <!--before是在执行UserService里add方法之前,aop介入-->
            <aop:before method="startTime" pointcut-ref="addAllMethod"/>
            <!--after是在执行UserService里add方法之后,aop介入-->
            <aop:after method="endTime" pointcut-ref="addAllMethod"/>
            <!--环绕是在执行UserService里add方法之前之后,都介入aop-->
            <aop:around method="aroundTest" pointcut-ref="addAllMethod"/>
        </aop:aspect>
    </aop:config>
</beans>


import org.aspectj.ProceedingJoinPoint;
import javax.xml.crypto.Data;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * Created by on 2018/12/22.
 */
//一把菜刀
public class Halder {
    public void startTime(){
        long startTime =System.currentTimeMillis();
        System.out.println("开始执行时间:"+new Date());
    }
    public void endTime(){
        long endTime =System.currentTimeMillis();
        System.out.println("结束执行时间:"+new Date());
    }

    //环绕通知
    public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint){
   //
   //   System.err.println("环绕通知进来的参数"+proceedingJoinPoint.getArgs());
   //   拿目标类
   //   System.err.println(proceedingJoinPoint.getTarget());
   //   拿目标方法
   //   System.err.println(proceedingJoinPoint.getSignature().getName());
        Object arg = proceedingJoinPoint.getArgs()[0];
        String s = (String)arg;
        System.err.println("原来的环绕通知进来的参数:"+s);
        Object[] args = proceedingJoinPoint.getArgs();
        args[0]="你好我是环绕通知修改后的参数:";
        
        try {
    //        System.err.println("我是调用:"+proceedingJoinPoint.getSignature().getName()+"之前的输出");
         // 去执行目标方法的处理,执行proceed()之后去调用add方法
            Object o = proceedingJoinPoint.proceed(args);
            List list = (List)o;
            list.clear();
            list.add("哈哈哈,我是修改后的返回参数");
            System.err.println(o);
    //        System.err.println("我是调用:"+proceedingJoinPoint.getSignature().getName()+"之后的输出");
            return o;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NeilNiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值