Spring核心讲解

1、Spring框架概述

  • Spring是轻量级的开源的JavaEE框架
  • Spring可以解决企业应用开发的复杂性
  • Spring有许多组成部分 核心IOC和AOP
  • IOC:控制反转 把创建对象的过程交给Spring进行管理
  • AOP: 面向切面 不修改源代码进行功能增强
  1. 方便解耦 简便开发
  2. AOP编程支持
  3. 方便unit测试
  4. 可以方便和其他框架进行集合
  5. 支持事务处理以及降低API的开发难度

2、入门案例

1、下载Spring5

spring 解决uncode转码问题_xml

2、选择地址进行下载

spring 解决uncode转码问题_spring_02

3、复制这个地址

spring 解决uncode转码问题_xml_03

下载地址https://repo.spring.io/ui/native/release/org/springframework/spring

4、源码解压

spring 解决uncode转码问题_spring 解决uncode转码问题_04

JAR包分为三类:

  • .jar基本jar包
  • .javadoc 文档包
  • .source源代码包

这里我们主要研究四个

  • Beans 对应IOC
  • Core 对应IOC
  • Context 对应上下文
  • Expression 对应表达式

5、JAR包导入

spring 解决uncode转码问题_spring_05

6、使用Spring方式创建对象

6.1 测试 使用普通类进行创建对象

/**
 * @author
 * @Package com.ljx.spring5
 * @date 2021-09-14 15:26
 */
public class User {
    public void add(){
        System.out.println("add....");
    }
}
User user = new User();

6.2 使用Spring方式创建

1.通过配置文件 在配置文件里面创建对象

<?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">
<!--  配置User创建  -->
    <bean id="user" class="com.ljx.spring5.User"></bean>
</beans>

通过这个bean实现了对象的创建

2、测试对象的编写

package com.ljx.test;

import com.ljx.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author ljx
 * @Package com.ljx.test
 * @date 2021-09-14 15:45
 */
public class TestSpring5 {

    @Test
    public void test01(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        User user = applicationContext.getBean("user", User.class);
        System.out.println(user);
    }

}

测试Spring创建对象

3、IOC容器

3.1、IOC底层原理

1、什么是IOC

  • 控制反转也可以被称为依赖注入,把对象创建和对象之间的调用过程交给Spring进行管理
  • 为什么使用:为了降低耦合度
  • 上面的案例就是IOC的实现

2、IOC底层原理

(1)xml解析、工厂模式、反射

原始创建对象的方式

spring 解决uncode转码问题_spring 解决uncode转码问题_06

使用IOC的过程

spring 解决uncode转码问题_xml_07

3、IOC接口

1、IOC思想基于IOC容器,IOC容器底层就是对象工厂

2、Spring提供了两种方式

  • BeanFactory: IOC最基本的实现方式 是Spring 内部使用的接口 不提供开发使用

加载配置文件的时候是不会创建对象,只有获取对象才会去创建对象

  • ApplicationContext:BeanFactory接口的子接口 一般是由开发人员进行使用的

加载配置文件的时候 就会把对象给创建出来

3、ApplicationContext实现类

spring 解决uncode转码问题_spring_08

  • FileSystemXmlApplicationContext 指的是配置文件的带盘符的路径
  • ClassPathXmlApplicationContext 指的是配置文件的类路径 也就是src的路径

4、BeanFactory接口实现类

spring 解决uncode转码问题_spring_09

3.2、IOC接口(BeanFactory)

IOC操作Bean管理

1、Bean管理

  • Spring创建对象
  • Spring属性注入

2、Bean管理的两种操作方式

(1) 基于xml配置文件的方式进行注入

(2)使用注解的方式进行注入

3、基于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">
<!--  配置User创建  -->
<!--  bean的属性讲解
    id: 给一个对象起一个表示  根据这个表示可以获取对象
    class:属性 类的全路径
  -->
<!-- 没有设置参数的时候默认会执行无参的构建   -->
    <bean id="user" class="com.ljx.spring5.User">
    </bean>
<!--  基于XML的方式注入属性
    1、DI 依赖注入 就是注入属性  是IOC的一种具体实现  前提是在对象已经创建的基础上
-->
</beans>

3.3、IOC的Bean管理(基于XML)

1、在配置文件中先实现对象创建

2、注入属性

通过set方法注入
<?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">
<!--  配置User创建  -->
<!--  bean的属性讲解
    id: 给一个对象起一个表示  根据这个表示可以获取对象
    class:属性 类的全路径
  -->
<!-- 没有设置参数的时候默认会执行无参的构建   -->
    <bean id="user" class="com.ljx.spring5.User">
        <property name="userName" value="ljx"/>
    </bean>
<!--  基于XML的方式注入属性
    1、DI 依赖注入 就是注入属性  是IOC的一种具体实现  前提是在对象已经创建的基础上
name 是属性名称  value是设置的值
-->
</beans>
前提是对象里面要有Set方法  注入属性就相当于设置对应的Set方法

编写对应的测试类

@Test
    public void test02(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        User user = applicationContext.getBean("user", User.class);
        String userName = user.getUserName();
        System.out.println(userName);
    }
通过构造方法注入
<?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">
<!--  配置User创建  -->
<!--  bean的属性讲解
    id: 给一个对象起一个表示  根据这个表示可以获取对象
    class:属性 类的全路径
  -->
<!-- 没有设置参数的时候默认会执行无参的构建   -->
    <bean id="user" class="com.ljx.spring5.User">
<!--        <property name="userName" value="ljx"/>-->
        <constructor-arg name="userName" value="ljx"/>
    </bean>
<!--  基于XML的方式注入属性
    1、DI 依赖注入 就是注入属性  是IOC的一种具体实现  前提是在对象已经创建的基础上
-->
</beans>

编写对应测试类

@Test
    public void test02(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        User user = applicationContext.getBean("user", User.class);
        String userName = user.getUserName();
        System.out.println(userName);
    }

注入其他类型的属性

<?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">
    <!--  配置User创建  -->
    <!--  bean的属性讲解
        id: 给一个对象起一个表示  根据这个表示可以获取对象
        class:属性 类的全路径
      -->
    <!-- 没有设置参数的时候默认会执行无参的构建   -->
    <bean id="user" class="com.ljx.spring5.User">
        <property name="userName" value="李加喜"/>
        <property name="address">
            <!--    为属性设置空值        -->
            <null/>
        </property>
        <!--   属性值设置了特殊符号     -->
        <!--        <constructor-arg name="userName" value="李加喜"/>-->
        <property name="userId">
            <value>
                <![CDATA[<<用户Id]>>]]>
            </value>
        </property>
    </bean>
    <!--  基于XML的方式注入属性
        1、DI 依赖注入 就是注入属性  是IOC的一种具体实现  前提是在对象已经创建的基础上
    -->
</beans>
注入属性-外部bean

创建dao和daoImpl

public interface UserDao {
    /**
     * 修改
     */
    void update();
}
public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("service add ....");
    }
}

创建service里面引入dao

public class UserService {
    // 创建UserDao类型的属性  生成对应的Set方法
    private UserDao userDao1;

    public UserDao getUserDao1() {
        return userDao1;
    }

    public void setUserDao1(UserDao userDao1) {
        this.userDao1 = userDao1;
    }

    public void add(){
        System.out.println("测试 add...");
        userDao1.update();
    }
}

配置文件里面进行引入

<?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">
    <!--  创建service和dao对象  -->
    <bean id="userService" class="com.ljx.service.UserService">
        <!--   name是属性的名字  res是类里面属性的名称     -->
        <property name="userDao1" ref="userDao"/>
    </bean>
    <!----这里引入的是接口的实现类接口是不允许进行实例化的----!>
    <bean id="userDao" class="com.ljx.dao.UserDaoImpl"/>
</beans>
FactoryBean

Spring里面有两种Bean 一种普通的一种工厂Bean(FactoryBean)

  • 普通Bean定义的类型就是自己定义的类型
  • 工厂Bean 在配置文件里面定义的bean类型可以和返回类型不一样

第一步:创建一个类 让这个类作为工厂bean 实现接口FactoryBean

第二步:实现接口里面的方法 在实现的方法中定义返回的bean类型

package com.ljx.factorybean;

import com.ljx.spring5.User;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author
 * @Package com.ljx.factorybean
 * @date 2021-09-16 15:08
 */
public class MyBean implements FactoryBean<User> {

    /**
     * 定义返回的Bean
     * @return
     * @throws Exception
     */
    @Override
    public User getObject() throws Exception {
        User user = new User();
        user.setUserName("ljx");
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

配置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">
    <bean id="myBean" class="com.ljx.factorybean.MyBean"></bean>
</beans>

编写测试类

@Test
    public void testCollection() {
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        User myBean = applicationContext.getBean("myBean", User.class);
        System.out.println(myBean);
    }
bean的作用域

可以设置 bean是单实例还是多实例

Spring默认是单实例

@Test
    public void testBook(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        Book book = applicationContext.getBean("book", Book.class);
        System.out.println(book);
        Book book1 = applicationContext.getBean("book", Book.class);
        System.out.println(book.equals(book1));
    }

返回结果是true 所以默认是单实例的

设置对象是多实例对象

设置配置文件

<?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">
    <bean id="myBean" class="com.ljx.factorybean.MyBean"></bean>
    <bean id="book" class="com.ljx.factorybean.Book" scope="prototype"></bean>
</beans>
prototype:是多实例
singleton:是单实例

编写测试文件

@Test
    public void testBook(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        Book book = applicationContext.getBean("book", Book.class);
        System.out.println(book);
        Book book1 = applicationContext.getBean("book", Book.class);
        System.out.println(book.equals(book1));
    }
是false  说明当前是多实例的
bean的生命周期

1、生命周期

从对象的创建到对象的销毁的过程

2、bean的生命周期

  • 通过构造器创建bean的实例(无参构造)
  • 为bean的属性设置值和对其他bean进行引用(调用set方法)
  • 调用bean的初始化方法
  • bean可以进行获取了
  • 当容器管理之后 调用bean的销毁方法
package com.ljx.bean;

/**
 * @author 李加喜
 * @Package com.ljx.bean
 * @date 2021-09-16 21:48
 */
public class Orders {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("第二步 调用set设置值");
        this.name = name;
    }

    public Orders(String name) {
        this.name = name;
    }
    public Orders(){
        System.out.println("第一步 执行无参数构造 构造创建bean");
    }

    // 创建初始化方法
    public void initMethod(){
        System.out.println("第三步调用自定义初始化方法");
    }
    /**
     * 执行销毁方法
     */
    public void destroyMethod(){
        System.out.println("第五步 调用销毁方法");
    }
}

编配配置文件

<?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">
    <bean id="orders" class="com.ljx.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="名称"/>
    </bean>
</beans>

测试

@Test
    public void testOrders(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        Orders orders = applicationContext.getBean("orders", Orders.class);
        System.out.println("第四步 获取bean实例对象");
        System.out.println(orders);
        // 手动销毁
        applicationContext.close();
    }
结果
第一步 执行无参数构造 构造创建bean
第二步 调用set设置值
第三步调用自定义初始化方法
第四步 获取bean实例对象
com.ljx.bean.Orders@64f6106c
第五步 调用销毁方法

3、bean的后置处理器 (总共七步)

  • 通过构造器创建bean的实例(无参构造)
  • 为bean的属性设置值和对其他bean进行引用(调用set方法)
  • 将bean的实例传递给后置处理器(postProcessBeforeInitialization)
  • 调用bean的初始化方法
  • 将bean的实例传递给后置处理器(postProcessAfterInitialization)
  • bean可以进行获取了
  • 当容器管理之后 调用bean的销毁方法

创建类 实现接口BeanPostProcessor 创建后置处理器

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前 postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后postProcessAfterInitialization");
        return bean;
    }
}

配置文件

<?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">
    <bean id="orders" class="com.ljx.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="名称"/>
    </bean>
    <bean id="beanPost" class="com.ljx.bean.MyBeanPost"></bean>
</beans>

这样容器里面的所有Bean都会执行这个方法

基于xml的自动装配
  1. 根据指定的装配规则(属性名称/属性类型) 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="orders" class="com.ljx.bean.Orders" init-method="initMethod"
          destroy-method="destroyMethod">
        <property name="name" value="名称"/>
    </bean>
    <bean id="beanPost" class="com.ljx.bean.MyBeanPost"></bean>
    <!-- byName 根据属性名进行注入  注入的值的bean的id要和类的属性名称一致
         byType 根据类型进行注入  找到属性的类型在管理的对象里面进行注入 
          注意 如果出现相同类型的两个bean 就会出去错误 -->
    <bean id="emp" class="com.ljx.bean.Emp" autowire="byName">
        <!--        <property name="dept" ref="dept"/>-->
    </bean>
    <bean id="dept" class="com.ljx.bean.Dept"></bean>
</beans>

类型

package com.ljx.bean;

import java.util.Date;

/**
 * @author 李加喜
 * @Package com.ljx.bean
 * @date 2021-09-16 22:59
 */
public class Emp {
    private Dept dept;

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "dept=" + dept +
                '}';
    }
    public  void test(){
        System.out.println(dept);
    }
}

public class Dept {
    @Override
    public String toString() {
        return "Dept{}";
    }

}
package com.ljx.bean;

import java.util.Date;

/**
 * @author 李加喜
 * @Package com.ljx.bean
 * @date 2021-09-16 22:59
 */
public class Emp {
    private Dept dept;

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "dept=" + dept +
                '}';
    }
    public  void test(){
        System.out.println(dept);
    }
}

public class Dept {
    @Override
    public String toString() {
        return "Dept{}";
    }

}

3.4、IOC的Bean管理()基于注解

1、创建对象提供的注解

  • @Service
  • @Component
  • @Controller
  • @Reposibility

上面的四个注解都可以用来创建bean实例

2、基于注解实现对象的创建

第一步、引入依赖

spring-aop-5.2.6.RELEASE.jar引入这个jar包

第二步、开启组件扫描

<?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 id="dept" class="com.ljx.bean.Dept"></bean>
	<!--    开启组件扫描		-->
    <context:component-scan base-package="com.ljx"/>
</beans>

第三步 创建类 在类上面添加创建对象的注解

@Component(value="可以是自定义名称 默认是类的首字母小写")
public class Pfm {
    public void add(){
        System.out.println("add");
    }
}

第四步 测试

@Test
    public void pfm(){
        // 加载Spring的配置文件  在src下直接写名称 不在的话需要写具体路径
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        Pfm pfm = applicationContext.getBean("pfm", Pfm.class);
        pfm.add();
    }

3、组件扫描的细节配置

<!--    开启组件扫描-->
    <context:component-scan base-package="com.ljx"/>
<!--  use-default-filters="false" 表示不使用默认的filter 自己配置filter
      context:include-filter  设置需要扫描那些配置类  这类的配置只会扫描带有Controller的类
-->
    <context:component-scan base-package="com.ljx" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
<!--只扫描包含Component的类-->
    <context:component-scan base-package="com.ljx" >
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>

4、基于注解的方式实现属性注入

1、@AutoWired 根据类型进行注入

2、@Qualifier 根据属性名称注入

3、@Resource 可以根据类型也可以根据名称

4、@Value 根据普通类型注入

public interface UserDao {
    /**
     * 修改
     */
    void update();
}
@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("service add ....");
    }
}
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void add(){
        System.out.println("测试 add...");
        userDao.update();
    }
}
 @Test
    public void setUserService(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");
        //获取配置创建的对象  这个user是对应的bean的Id
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.add();
    }

开启组件扫描

<?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">
<!--    开启组件扫描-->
    <context:component-scan base-package="com.ljx"/>
</beans>

使用@Qualifier指定名称注入 防止接口有多个实现类

@Repository(value = "userDao1")
public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("service add ....");
    }
}
@Service
public class UserService {
    //@Autowired
    @Qualifier(value = "userDao1")
    private UserDao userDao;

    public void add(){
        System.out.println("测试 add...");
        userDao.update();
    }
}

完全注解开发

1、配置类

@Configuration
@ComponentScan(basePackages = {"com.ljx"})
public class SpringConfig {
    
}
取代了xml注解

2、测试

@Test
    public void config(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

4、AOP

  • 面向切面编程(面向方面编程)
  • 降低耦合度 提高开发
  • 主要就是方便在已有的功能上面添加新的功能 降低代码的耦合度

AOP底层使用的动态代理的方式进行实现的

  • 有接口的动态代码 JDK的动态代理

spring 解决uncode转码问题_spring 解决uncode转码问题_10

  • 没有接口的动态代理 CGLIB动态代理

spring 解决uncode转码问题_xml_11

1、创建Interface接口和接口的实现类

public interface UserDao {
    public Integer add(int a,int b);

    public void update(String id);
}
public class UserDaoImpl implements UserDao {
    @Override
    public Integer add(int a, int b) {
        return a+b;
    }

    @Override
    public void update(String id) {
        System.out.println(id);
    }
}
public class JDKProxy {
    public static void main(String[] args) {
        UserDaoImpl userDao = new UserDaoImpl();
        // 创建接口实现类的代理对象
        Class[] interfaces = {UserDao.class};
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,
                new UserDaoProxy(userDao));
        Integer add = dao.add(1, 2);
        System.out.println(add);
    }
}

class UserDaoProxy implements InvocationHandler {
    // 需要把谁的代理对象要把 对象传递过来
    private Object object;
    public UserDaoProxy(Object object){
        this.object=object;
    }

    // 增强逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法之前
        System.out.println("方法之前执行"+method.getName()+"传递的参数是"+ Arrays.toString(args));
        // 被增强的方法执行
        Object res = method.invoke(object, args);
        System.out.println(res);
        // 方法之后
        System.out.println("方法之后执行"+object);
        return res;
    }
}
结果
方法之前执行add传递的参数是[1, 2]
3
方法之后执行com.ljx.UserDaoImpl@355da254
3

AOP专业术语

  • 连接点 类里面那些方法可以被增强 方法被称为连接点
  • 切入点 实际被增强的方法
  • 通知(增强) 实际增强的逻辑部分就是通知(前置 后置 环绕 异常 最终)
  • 切面 将通知应用到切入点的过程

2、AOP的JDK动态代理案例

使用newProxyInstance方法,方法有三个参数 (类加载器、增强方法所在的类 这个类所在的接口 支持多个接口、实现InvocationHandle接口 创建代理对象写增强方法)

3、Spring框架基本使用Aspectj实现AOP操作

一般和Spring框架一起使用实现Aop的功能 一般开发中是基于注解的方式进行使用

4、在项目中引入AOP的依赖

spring 解决uncode转码问题_xml_12

5、切入点的表达式

语法结构:execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])

execution(*com.ljx.dao.BookDao.add(…))

execution(*com.ljx.dao.BookDao. * (…)) BookDao下面的左右方法

execution(*com.ljx.dao. * . * (…)) 下面的所有的方法

6、使用注解的方式实现Aop

在增强类里面创建方法 让不同的代表不同的通知类型

  • 开启注解扫描
  • 使用注解创建User和UserProxy
  • 在增强类上面添加注解@Aspect
  • 在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: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.ljx"/>
    <!--    开启Aspect生成代理对象  扫描的类上面有@Aspect就生成代理对象-->
    <aop:aspectj-autoproxy/>
</beans>

设置类

// 被增强类
@Component
public class User {
    public void add() {
        System.out.println("add...");
    }
}
@Component
@Aspect
public class UserProxy {
    // 在
    public void before() {
        System.out.println("before");
    }
}

配置不同类型的通知

在增强类里面在作为通知类型的注解使用切入点表达式配置

package com.ljx.aopanno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

// 前置通知 增强类
@Component
@Aspect
public class UserProxy {
    /**
     * 相同切入点抽取
     */
    @Pointcut(value = "execution(* com.ljx.aopanno.User.add(..))")
    public void pointDemo(){

    }

    /**
     * 前置通知
     */
    @Before(value = "pointDemo()")
    public void before() {
        System.out.println("before");
    }

    /**
     * 后置通知
     */
    @After(value = "pointDemo()")
    public void after(){
        System.out.println("after");
    }

    /**
     * 返回值之后执行
     */
    @AfterReturning(value = "pointDemo()")
    public void afterReturning(){
        System.out.println("afterReturning");
    }

    /**
     * 出现异常的时候进行通知
     */
    @AfterThrowing(value = "pointDemo()")
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }

    /**
     * 环绕通知
     */
    @Around(value = "pointDemo()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around前");
        // 执行增强的方法
        proceedingJoinPoint.proceed();
        System.out.println("around后");
        return new Object();
    }
}

有多个增强类对同一个方法进行增强 设置优先级

在增强类里面添加注解**@Order**(数字类型的值) 数字类型值越小优先级越高

5、事务操作

事务是数据库操作最基本的单元 逻辑上的一组操作 要么都成功要么都失败

四大特性: ACID

  • 原子性 要么都成功呢要么都失败
  • 一致性 操作前后数据不变
  • 隔离性 多事务操作的时候不会产生影响
  • 持久性 表中数据真正发生改变

事务的操作

基本使用的声明式事务(基于AOP的)

事务的传播行为:

  • 多事务方法直接进行调用,这个过程中事务是如何进行管理的
  • REQUIRED 如果事务在运行 当前方法就在这个事务内运行 否则就启动一个新的事务
  • REQUIRED_NEW 当前的方法必须启动新的事务 并在他自己的事务内运行 如果有事务正在运行 应该将他挂起
  • SUPPORTS 如果事务在运行 当前的方法就在这个事务内运行 否则可以不运行在事务内
  • NOT_SUPPORTS 当前方法不应该在事务中运行 如果有运行的事务 将他挂起
  • MANDATORY 当前的方法必须运行在事务内部 如果没有正在运行的事务 就抛出异常
  • NEVER 当前的方法不应该运行在事务中 如果有运行的事务 就抛出异常
  • NESTED 如果有事务在运行 当前的方法就应该在这个事务的嵌套事内运行 否则就启动一个新的事务

事务隔离级别:

  • 多事务之间不会产生影响 不考虑隔离性产生很多问题 比如
  • 脏读:多个事务之间一个未提交的事务获取了另一个未提交的事务数据

举例:A将数据从1改为2 不进行读取的时候获取的是2 但是这个时候A进行了回滚 那么B读的数据就是脏数据

  • 不可重复读:一个未提交的事务读取了另一个事务修改的数据

A和B都在读取数据 A将数据从100改为200 然后事务进行了提交 但是B在读取数据的时候没有提交数据 导致B一致获取的A提交的数据 导致可能每一次读取的数据都会发生改变

  • 幻读 一个未提交的事务获取了另一个提交事务的数据

设置事务隔离级别

  • READ UNCOMMITTED(读未提交) 脏读 不可重复读 幻读
  • READ COMMINTED(读已提交) 不可重复读 幻读
  • READATABLE READ(可重复度) 幻读
  • SERIALIZABLE(串行化)