spring框架 IOC(注解方式开发)AOP(xml方式开发)

spring IOC注解方式开发

spring框架4.X版本以上开发,IOC注解方式开发要比XML方式开发多导入一个AOP的jar包,主要是一些类的属性封装到了AOP的jar包的接口中。

注解开发实例

  1. 导入相关jar包
    在这里插入图片描述
  2. 创建接口UserDao.java 和 实现类UserDaoImpl.java,并在类上添加注解
接口
public interface UserDao {
    public void save();
}

================================================
实现类
import com.web.dao.UserDao;
import org.omg.IOP.ComponentIdHelper;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.InitBinder;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

//@Component(value = "userDao")
//相当于xml方式:<bean id="userDao" class="com.web.dao.impl.UserDaoImpl" />
@Repository(value = "userDao")
@Scope(value = "prototype")
public class UserDaoImpl implements UserDao {

    @PostConstruct
    public void init(){
        System.out.println("对象初始化调用该方法");
    }
    //当给属性赋值的时候,可以有set方法,有的时候,把注解放在set方法上,或者放在属性上都可以。
    //也可以没有set方法,没有的时候,直接把注解放在属性上就可以
    
    //    @Value(value = "王二小")
    private String name;
//    @Autowired
//    @Qualifier(value = "car")
//    @Resource(name = "car")
    private Car car;

    @Value(value = "哈哈哈")
    public void setName(String name) {
        this.name = name;
    }

//    @Autowired
//    @Qualifier(value = "car")
    @Resource(name = "car")
    public void setCar(Car car) {
        this.car= car;
    }

    @Override
    public void save() {
        System.out.println("执行了UserDao中的方法");
    }

    @PreDestroy
    public void distory(){
        System.out.println("对象销毁调用该方法");
    }
}
  1. 配置applicationContext.xml文件,放在src目录下

注意:要引入context约束
在spring的解压路径下spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html 的html中有scamer约束,复制下来就可以。

<?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" 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"> <!-- bean definitions here -->
	
	<!--要引入context约束-->
    <!--使用注解的方式,先进行扫描该包下的加注解@Component的类-->
    <!--注意:是扫描的扫描类上的注解-->
    <context:component-scan base-package="com"  ></context:component-scan>

    <!--不扫描类的情况下,使用注解进行属性注入
		@Resource  @Autowited @Value @Qualifier 
-->
    <context:annotation-config></context:annotation-config>

</beans>

  1. 创建测试类SpringDemo.java
import com.service.UserService;
import com.web.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo {
    @Test
    /**
     * 使用注解方式IOC
     */
    public void demo1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
//        userDao.save();
        UserDao userDao2 = (UserDao) applicationContext.getBean("userDao");
//        userDao.save();
        System.out.println(userDao);
        System.out.println(userDao2);
        ((ClassPathXmlApplicationContext) applicationContext).close();
    }

    @Test
    /**
     * 使用注解方式IOC
     * 属性的注入
     */
    public void demo2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.save();
    }
}

spring 的IOC注解方式的详解

类的注解,Bean的管理

@Component注解:组件
这个注解有三个衍生的注解,功能和@Component差不多

  1. @Controller:web层注解
  2. @Service:service层注解
  3. @Repository:dao层注解

作用:修饰一个类,将这个类交给spring框架管理
为什么分开呢?因为便于框架的分层结构的清晰,每一层的注解会有一些相对应该层的属性。

@Component注解是可以修饰任何一层的类的,注解上该类之后,
@Component(value="userDao")------ value可以省略 @Component("userDao")
相当于xml配置文件中
<bean id="userDao" class="xxx.xxx.xx" />

属性注入的注解

  • 基本类型属性的注入

@Value(value="xxx") ,------其中value可以省略 @Value("xxx")
注解可以写在属性上,也可以写在set方法上

  1. 当没有set方法时候,也可以,将注解写在属性上
  2. 当有set方法的时候,注解可以写在属性上,也可以写在set方法上
  • 引用类型属性的注入
  • @ Autowired
    该注解默认设置对象属性的值,是按照类型完成属性的注入。
    我们习惯按照名称完成属性的注入:必须让
  • @Autowired 和 @Qualifiter
    注解一起使用,就可以按照名称注入值
  • @Resource(name="user") ,完成属性的注入,是按照名称来注入的。这个是spring实现了其他的接口规范实现的。
  • 注解可以写在属性上,也可以写在set方法上
    • 当没有set方法时候,也可以,将注解写在属性上
    • 当有set方法的时候,注解可以写在属性上,也可以写在set方法上

<bean id="car" class="com.domain.Car"></bean>
<bean id="customer" class="com.domain.Customer">
    <property name="name" value="王五"></property>
    <property name="car" ref="car"></property>
</bean>
id=car  ,ref=car,值必须相等,如果id=caee,那么ref=caee才可以,
这就是引用类型按名称注入。
如果使用注解的方式
@Autowired
@Qualifier(value = "car"),同时使用才可以,而Car对象的@Component(value="car"),
value必须一样才可以。
如果只使用@Autowired注解,名字不同也没事,因为是按照类型完成属性的注入。

Bean其他的注解

  • Bean的生命周期
  1. @PostConstruct:初始化方法,在一个方法上写上该注解,类初始化的时候就会调用该方法
  2. @PreDestory:销毁方法,在一个方法上写上该注解,类销毁的时候就会调用该方法
  • Bean的作用范围

@Scope(value="xxx")
值由以下几个:
singleton :默认的,单例模式创建对象,Spring会采用单例模式创建这个对象。
prototype :多例模式。(Struts2和Spring整合一定会用到)
request :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。
session :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。
globalsession :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相对于session。

IOC的xml方式和注解方式开发的比较

适用场景

  1. xml方式:适用于任何场景
    优点:便于后期的维护,结构清晰
  2. 注解方式:有些地方用不了,这个类不是自己提供的,没办法加注解
    优点:开发方便

xml和注解整合开发

XML用来管理Bean,注解完成属性的注入

当这种情况下,Bean的管理是使用xml方式实现的,所以不用扫描类的情况下使用注解完成属性注入,必须在applicationContext.xml文件中配置

  <!--不扫描类的情况下,使用注解进行属性注入-->
    <context:annotation-config></context:annotation-config>

spring的AOP(XML方式开发)

AOP的概述

面向切面编程是一种开发思想,产生代理对象而spring框架是运用这种思想最好的框架。增强某种方法的功能,spring框架不需要自己去写代码,根据一些配置文件就能自动生成。在不能继承,不能修改源码的情况下非常方便。
AOP是OPP的扩展和延伸,解决OPP(面向对象的开发)开发中遇到的问题。

在这里插入图片描述
在这里插入图片描述

spring的底层AOP实现原理(了解)

底层的实现主要是根据产生代理对象实现功能增强的一种切面式开发方式

  • 产生代理对象有两种方式。
  1. JDK动态代理。jdk提供的只能对实现接口的类产生代理对象。
  2. 第三方提供的 Cglib 动态代理。对没有实现接口的类产生代理对象, 生成子类的对象(类似于javassist第三方代理技术)。

JDK动态代理

public class MyProxy implements InvocationHandler {
    Customer customer;
    public MyProxy(Customer customer) {
        this.customer = customer;
    }
    public Customer createProxy(){
        Customer o = (Customer)Proxy.newProxyInstance(MyProxy.class.getClassLoader(), CustomerImpl.class.getInterfaces(),this);
        return o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o = null;
        if (method.getName().equals("save")){
            //增强功能
            System.out.println("权限校验");
            o = method.invoke(customer, args);
        }else {
            o = method.invoke(customer,args);
        }
        return o;
    }
}
====================================================================
public class springDemo2 {
    @Test
    //不加强运行
    public void demo1(){
        CustomerImpl customer = new CustomerImpl();
        customer.save();
    }
    @Test
    //加强功能后运行
    public void demo2(){
        MyProxy myProxy = new MyProxy(new CustomerImpl());
        Customer customer = myProxy.createProxy();
        customer.save();
    }
}

Cglib动态代理

public class CglibProxy implements MethodInterceptor {
    Customer customer;
    public CglibProxy(Customer customer){
        this.customer = customer;
    }

    public Customer createCglibProxy(){
        //创建Cglib的核心类对象
        Enhancer enhancer = new Enhancer();
        //设置目标类,也就是设置目标类,要代理哪个对象,(生成代理对象是其目标类的子类)
        enhancer.setSuperclass(customer.getClass());
        //设置回调(MethodInterceptor类似于InvocationHandle对象)
        enhancer.setCallback(this);
        //返回代理对象
        Customer o = (Customer)enhancer.create();
        return o;
    }


    @Override
    //类似于invoke()方法
    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(method.getName().equals("find")){
            //增强
            System.out.println("权限校验");
            //执行原方法,使用方法的代理对象methodProxy,不是用method
            //第一个参数:生成的代理对象
            //第二个参数:原方法里面的参数,没有默认为null
            return methodProxy.invokeSuper(proxy, objects);
        }
        return methodProxy.invokeSuper(proxy,objects);
    }
}
========================================================================
public class springDemo2 {
    @Test
    //使用Cglib的方式产生动态代理对象
    public void demo3(){
        CglibProxy cglibProxy = new CglibProxy(new CustomerImpl());
        Customer customer = cglibProxy.createCglibProxy();
        customer.find();
    }
}


spring的AOP的开发

spring的AOP的简介

AOP是一种由AOP联盟组织提出的思想,而spring框架是将这种思想运用最好的框架

  • spring框架中实现AOP思想的开发方式
    • spring自己的AOP的实现方式(繁琐,以弃用)
    • spring引入的第三方的AspectJ框架(是AOP的框架),spring引入AspectJ作为自身的AOP的开发。(推荐使用)

spring的AOP开发中的相关术语

在这里插入图片描述

spring的AOP的入门

创建web项目,引入相关的jar包

在这里插入图片描述

引入spring的配置文件

<?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: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="customer" class="com.dao.impl.CustomerImpl"></bean>

    <!--设置切面类:将切面类交给spring管理-->
    <bean id="myAspectXML" class="com.web.spring.MyAspectXML"></bean>

    <!--通过AOP配置,生成要增强类的代理对象-->
    <!--底层根据要增强的类是否实现了接口,如果实现了接口,用jdk提供的动态代理生成对象,
        如果没有实现接口,用cglib动态代理生成代理对象。-->
    <aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->
        <aop:pointcut id="pointcut1" expression="execution(* com.dao.impl.CustomerImpl.find(..)) "/>
        <aop:pointcut id="pointcut2" expression="execution(* com.dao.impl.CustomerImpl.delete(..)) "/>
        <aop:pointcut id="pointcut3" expression="execution(* com.dao.impl.CustomerImpl.save(..)) "/>
        <aop:pointcut id="pointcut4" expression="execution(* com.dao.impl.CustomerImpl.update(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中-->
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="pointcut1" ></aop:before>
            <!--后置通知,可以接收原方法中返回的值-->
            <aop:after-returning method="after" pointcut-ref="pointcut2" returning="aa"></aop:after-returning>
            <!--环绕通知,可以不运行原方法-->
            <aop:around method="around" pointcut-ref="pointcut3" ></aop:around>
            <!--异常通知,当原方法有异常的时候,输出异常信息-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"></aop:after-throwing>
            <!--最终通知,不论是否发生异常,都会通知-->
            <aop:after method="afterAll" pointcut-ref="pointcut4"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

编写目标类并完成配置

=====接口===================
package com.dao;

public interface Customer {
    public void save();
    public void find();
    public void update();
    public String delete();
}
==============================================================
========实现类========
package com.dao.impl;

import com.dao.Customer;

public class CustomerImpl implements Customer {
    @Override
    public void save() {
        System.out.println("保存用户");
    }

    @Override
    public void find() {
        System.out.println("查找客户");
    }

    @Override
    public void update() {
        System.out.println("更新用户");
//        int a = 3/0;
    }

    @Override
    public String delete() {
        System.out.println("删除用户");
        return "哈哈哈";
    }
}

编写测试类

//这两行的作用:spring整合junit,加载spring工厂,ApplicationContext,并加载配置文件。
//优点:就不用每次都生成工厂,使用getBean()获得对象了。
//需要导入spring-test-4.2.4.RELEASE.jar包

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
package com.web.spring;

import com.dao.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

//这两行的作用:spring整合junit,加载spring工厂,ApplicationContext,并加载配置文件。
//优点:就不用每次都生成工厂,使用getBean()获得对象了。
//需要导入spring-test-4.2.4.RELEASE.jar包
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class springDemo3 {
    @Resource(name = "customer")
    public Customer customer;

    @Test
    public void demo(){
        customer.find();
        customer.delete();
        customer.save();
        customer.update();
    }
}

编写切面类

package com.web.spring;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 增强方法的类,切面类
 */
public class MyAspectXML {

    public void before(){
        System.out.println("执行前增强了权限校验的功能");
    }

    public void after( Object aa){
        System.out.println("执行之后增强了记录日志的功能"+aa);
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前通知");
        proceedingJoinPoint.proceed(); //执行原方法,也可以选择不执行
        System.out.println("环绕后通知");
    }

    public void afterThrowing( Throwable ex) throws Throwable {
        System.out.println("异常抛出通知==="+ex.getMessage());
    }

    public void afterAll() {
        System.out.println("最终通知,无论是否有异常,都会通知===");
    }
}

通过AOP的配置(基于AspectJ的方式的XML方式)完成目标类代码的增强

 <!--设置目标对象:要增强的对象-->
    <bean id="customer" class="com.dao.impl.CustomerImpl"></bean>

    <!--设置切面类:将切面类交给spring管理-->
    <bean id="myAspectXML" class="com.web.spring.MyAspectXML"></bean>

    <!--通过AOP配置,生成要增强类的代理对象-->
    <!--底层根据要增强的类是否实现了接口,如果实现了接口,用jdk提供的动态代理生成对象,
        如果没有实现接口,用cglib动态代理生成代理对象。-->
    <aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->
        <aop:pointcut id="pointcut1" expression="execution(* com.dao.impl.CustomerImpl.find(..)) "/>
        <aop:pointcut id="pointcut2" expression="execution(* com.dao.impl.CustomerImpl.delete(..)) "/>
        <aop:pointcut id="pointcut3" expression="execution(* com.dao.impl.CustomerImpl.save(..)) "/>
        <aop:pointcut id="pointcut4" expression="execution(* com.dao.impl.CustomerImpl.update(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中-->
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="pointcut1" ></aop:before>
            <!--后置通知,可以接收原方法中返回的值-->
            <aop:after-returning method="after" pointcut-ref="pointcut2" returning="aa"></aop:after-returning>
            <!--环绕通知,可以不运行原方法-->
            <aop:around method="around" pointcut-ref="pointcut3" ></aop:around>
            <!--异常通知,当原方法有异常的时候,输出异常信息-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"></aop:after-throwing>
            <!--最终通知,不论是否发生异常,都会通知-->
            <aop:after method="afterAll" pointcut-ref="pointcut4"></aop:after>
        </aop:aspect>
    </aop:config>

spring中的通知类型

前置通知

<aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->
        <aop:pointcut id="pointcut1" expression="execution(* com.dao.impl.CustomerImpl.find(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中-->
            <!--前置通知-->
            <aop:before method="before" pointcut-ref="pointcut1" ></aop:before>
            </aop:after>
        </aop:aspect>
    </aop:config>

后置通知

后置通知,可以获得原方法返货的值
注意:在xml文件中的returning属性的值是自己定义的,在对应的切面类的方法中的参数名要与returning属性中的值一样才可以
public void after( Object aa){
System.out.println(“执行之后增强了记录日志的功能”+aa);
}

<aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->    
        <aop:pointcut id="pointcut2" expression="execution(* com.dao.impl.CustomerImpl.delete(..)) "/>  
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">  
            <!--后置通知,可以接收原方法中返回的值-->
            <aop:after-returning method="after" pointcut-ref="pointcut2" returning="aa"></aop:after-returning>   
        </aop:aspect>
</aop:config>

环绕通知

环绕通知,可以不用运行原方法,运行原方法如下:
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println(“环绕前通知”);
proceedingJoinPoint.proceed(); //执行原方法,也可以选择不执行
System.out.println(“环绕后通知”);
}

<aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->     
        <aop:pointcut id="pointcut3" expression="execution(* com.dao.impl.CustomerImpl.save(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中--> 
            <!--环绕通知,可以不运行原方法-->
            <aop:around method="around" pointcut-ref="pointcut3" ></aop:around>
        </aop:aspect>
    </aop:config>

异常通知

异常通知:当原方法执行发生异常的时候运行增强的方法,并且可以输出异常
注意:异常的输出,throwing属性的值是自己定义的,而切面类中的增强方法中的参数名要和自定义的throwing的属性值一样

<aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->
        <aop:pointcut id="pointcut4" expression="execution(* com.dao.impl.CustomerImpl.update(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中-->
            <!--异常通知,当原方法有异常的时候,输出异常信息-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

最终通知

<aop:config>
        <!--id:标签的唯一标识,自己随便写。-->
        <!--expression: 表达式,标识要增强的哪个类和哪个方法-->
        <!--配置那些类哪个方需要增强-->
        <aop:pointcut id="pointcut4" expression="execution(* com.dao.impl.CustomerImpl.update(..)) "/>
        <!--配置切面-->
        <aop:aspect ref="myAspectXML">
            <!--method:切面类的某一个方法名。 pointcut-ref:将这个切面类的方法应用在要增强的类的某个方法中-->
            <!--最终通知,不论是否发生异常,都会通知-->
            <aop:after method="afterAll" pointcut-ref="pointcut4"></aop:after>
        </aop:aspect>
</aop:config>

spring切入点的expression表达式的写法

基于execution()函数wang完成的。

  • 语法格式:execution(语法格式) *号代表任意字符
  • [权限修饰符] 方法返回值 包名.类名.方法名(参数)
    • public void com.dao.impl.CustomerImpl.update(…)
    • * com.*Dao.save(..),com包下的以Dao结尾的类中的save()方法
    • * com.dao.CustomerImpl+.save(..),com.dao包下的CustomerImpl类以及其所有子类下的save()方法
    • * com.dao..*.*(..),com.dao包以及这个包下的所有子包的所有类的所有方法
发布了60 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44142032/article/details/90704178