Java 基础

Spring 5 基础

2022-03-25  本文已影响0人  yjtuuige

一、Spring 概述

1.1 简介

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.17</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.17</version>
</dependency>

1.2 优点

1.3 组成

模块简介:

1.4 拓展

二、IOC 基础

2.1 IOC 理论推导

以前代码的实现方式:

  1. 创建 UserDao 接口:
public interface UserDao {
    public void getUser();
}
  1. 创建 UserDaoImpl 实现类 :
public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("获取用户数据");
    }
}
  1. 创建 UserService 业务接口:
public interface UserService {
    public void getUser();
}
  1. 创建 UserServiceImpl 业务实现类:
public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    
    @Override
    public void getUser() {
        userDao.getUser();
    }    
}    
  1. 测试:
public class MyTest {
    @Test
    public void testUser() {
        UserService service = new UserServiceImpl();
        service.getUser();
    }
}

以前增加需求的实现方式:

  1. 增加 Userdao 的实现类:
public class UserDaoOracleImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("Oracle数据");
    }
}
  1. 在 UserServiceImpl 实现类里,修改对应的实现:
public class UserServiceImpl implements UserService {
    // 修改UserDao的对应实现类
    private UserDao userDao = new UserDaoOracleImpl();
    
    @Override
    public void getUser() {
        userDao.getUser();
    }    
} 

解决方案:

public class UserServiceImpl implements UserService { 
    private UserDao userDao;

    // 利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}
public class MyTest {
    @Test
    public void testUser() {
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(new UserDaoImpl());
        service.getUser();
        // 用Oracle去实现
        service.setUserDao(new UserDaoOracleImpl());
        service.getUser();
    }
}

小结:

2.2 IOC 本质

三、Hello Spring

3.1 搭建环境

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.17</version>
</dependency>

3.2 编码代码

public class Hello {    
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public void show() {
        System.out.println("Hello " + str);
    }
}
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--bean就是java对象 , 由Spring创建和管理-->
    <!--id:对象名 class:类-->
    <bean id="hello" class="com.study.spring.pojo.Hello">
        <!--name:属性 value:值-->
        <property name="str" value="Spring"/>
    </bean>
</beans>
public class MyTest {
    @Test
    public void helloTest() {
        // 获取beans.xml:拿到Spring管理对象的容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // genBean:参数就是Spring配置文件中bean的id(对象名)
        Hello hello = (Hello) context.getBean("hello");
        hello.show();
    }
}

3.3 思考

这个过程就叫做 控制反转

3.4 修改之前代码

<bean id="userDaoImpl" class="com.study.spring.dao.UserDaoImpl"/>
<bean id="oracleImpl" class="com.study.spring.dao.UserDaoOracleImpl"/>
<bean id="service" class="com.study.spring.service.UserServiceImpl">
    <!--
        注意:
        name不是属性,而是set方法后面的那部分(首字母小写)
        ref:引用Spring容器中已经创建好的对象
        value:具体的值,基本数据类型
    -->
    <property name="userDao" ref="oracleImpl"/>
</bean>
@Test
public void testSpring() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    UserService service = (UserService) context.getBean("service");
    service.getUser();
}

小结:

四、IOC 创建对象方式

4.1 通过无参构造(默认)

public class User {
    private String name;

    public User() {
        System.out.println("无参构造");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("name=" + name);
    }
}
<bean id="user" class="com.study.spring.pojo.User">
    <property name="name" value="测试"/>
</bean>
@Test
public void testUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    // 在执行getBean的时候,user已经通过无参构造创建好了
    User user = (User) context.getBean("user");
    // 调用对象的方法
    user.show();
}

4.2 通过有参构造

public class User2 {
    private String name;

    public User2(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void show() {
        System.out.println("name=" + name);
    }
}

有参构造 beans.xml 的三种创建方式

  1. 下标赋值:
<!--1:下标赋值!-->
<bean id="user2" class="com.study.spring.pojo.User2">
    <constructor-arg index="0" value="有参测试"/>
</bean>
  1. 类型赋值:不建议使用,重复类型难以分辨
<!--2:类型赋值,不建议使用,重复类型难以分辨-->
<bean id="user2" class="com.study.spring.pojo.User2">
    <constructor-arg type="java.lang.String" value="有参测试2"/>
</bean>
  1. 参数名赋值:
<!--3:直接通过参数名来设置-->
<bean id="user2" class="com.study.spring.pojo.User2">    
    <constructor-arg name="name" value="有参测试3"/>
</bean>
@Test
public void testUser2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User2 user2 = (User2) context.getBean("user2");
    user2.show();
}

小结:

五、Spring 配置

5.1 别名

<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="user" alias="userNew"/>

5.2 Bean 的配置

<!--
    id:bean的唯一标识符,相当于对象名;
    如果没有配置id,name就是默认标识符,配置id后,name为别名;
    name可设多个别名,可以用逗号,分号,空格隔开;
    如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
    class:bean对象所对应的全限定名:包名 + 类名;
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.study.spring.pojo.Hello">
    <property name="name" value="Spring"/>
</bean>

5.2 import

<?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">
    <!--applicationContext.xml导入其它配置文件,合并成总配置文件-->
    <import resource="beans.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>
</beans>

六、依赖注入(DI)

6.1 构造器注入

6.2 set 注入(重点)

搭建环境

public class Address {
    private String address;
    // get、set、toString
}
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
    // get、set、toString
}
<?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="student" class="com.study.spring.pojo.Student">
        <!--1. 普通值注入,value-->
        <property name="name" value="学生1"/>
    </bean>
</beans>
@Test
public void studentTest() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("student");
    System.out.println(student.getName());
}
<!--bean类型,引用类-->
<bean id="address" class="com.study.spring.pojo.Address"/>
<bean id="student" class="com.study.spring.pojo.Student">
    <!--1. 普通值注入,value-->
    <property name="name" value="学生1"/>
    <!--2. Bean注入,ref-->
    <property name="address" ref="address"/>
    <!--3. 数组注入,array-->
    <property name="books">
        <array>
            <value>西游记</value>
            <value>红楼梦</value>
            <value>水浒传</value>
        </array>
    </property>
    <!--4. list-->
    <property name="hobbys">
        <list>
            <value>爬山</value>
            <value>阅读</value>
            <value>听歌</value>
        </list>
    </property>
    <!--5. Map-->
    <property name="card">
        <map>
            <entry key="建行" value="217842215439"/>
            <entry key="工行" value="54358942439"/>
        </map>
    </property>
    <!--6. Set-->
    <property name="games">
        <set>
            <value>LOL</value>
            <value>BOB</value>
            <value>COC</value>
        </set>
    </property>
    <!--7. null-->
    <property name="wife">
        <null/>
    </property>
    <!--8. Properties-->
    <property name="info">
        <props>
            <prop key="driver">com.mysql.cj.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306/数据库名?</prop>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>
@Test
public void studentTest() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("student");
    System.out.println(student.toString());

    /*
        Student{
            name='学生1',
            address=Address{address='null'},
            books=[西游记, 红楼梦, 水浒传],
            hobbys=[爬山, 阅读, 听歌],
            card={建行=217842215439, 工行=54358942439},
            games=[LOL, BOB, COC],
            wife='null',
            info={
                password=123456,
                driver=com.mysql.cj.jdbc.Driver,
                url=jdbc:mysql://localhost:3306/数据库名?,
                username=root
                }
          }
     */
    }
}

6.3 拓展方式注入

public class User {
    private String name;
    private int age;
    // // get、set、toString
}
<?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:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入,可以直接注入属性的值:property-->
    <bean id="user" class="com.study.spring.pojo.User" p:age="20" p:name="p 测试"/>
    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean id="user2" class="com.study.spring.pojo.User" c:age="18" c:name="c 测试"/>
</beans>
@Test
public void testUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user");
    User user2 = (User) context.getBean("user2");
    System.out.println(user);
    System.out.println(user2);
}
<!--头文件约束-->
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4 Bean 的作用域

单例模式(Spring 默认机制):singleton

<bean id="user" class="com.study.spring.pojo.User" scope="singleton"/>
@Test
public void testUser2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user");
    User user2 = (User) context.getBean("user");
    System.out.println(user==user2);    // true
}

原型模式:prototype

<bean id="user" class="com.study.spring.pojo.User" scope="prototype"/>
@Test
public void testUser2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user");
    User user2 = (User) context.getBean("user");
    System.out.println(user==user2);    // false
}

七、Bean 的自动装配

7.1 搭建测试环境

创建项目:

public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}
public class People {
    private Cat cat;
    private Dog dog;
    private String name;
    // get、set、toString
}
<?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="cat" class="com.study.spring.pojo.Cat"/>
    <bean id="dog" class="com.study.spring.pojo.Dog"/>
    <bean id="people" class="com.study.spring.pojo.People">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="name" value="测试"/>
    </bean>
</beans>
@Test
public void myTest() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    People people = (People) context.getBean("people");
    people.getCat().shout();
    people.getDog().shout();
}

7.2 ByName 自动装配

<!--byName:会自动在容器上下文中,查找和自己对象set方法后面的值对应的bean的id-->
<bean id="people" class="com.study.spring.pojo.People" autowire="byName">
    <property name="name" value="测试"/>
</bean>

7.3 ByType 自动装配

<bean class="com.study.spring.pojo.Cat"/>
<bean class="com.study.spring.pojo.Dog"/>
<!--byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!必须保证类型全局唯一!-->
<bean id="people" class="com.study.spring.pojo.People" autowire="byType">
    <property name="name" value="测试"/>
</bean>

小结:

7.4 使用注解实现自动装配

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启属性注解支持-->
    <context:annotation-config/>

</beans>

@Autowired

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
    // get、toString
}    
<!--开启属性注解支持-->
<context:annotation-config/>

<bean id="cat" class="com.study.spring.pojo.Cat"/>
<bean id="dog" class="com.study.spring.pojo.Dog"/>
<bean id="people" class="com.study.spring.pojo.People"/>

拓展

@Qualifier

<bean id="cat1" class="com.study.spring.pojo.Cat"/>
<bean id="dog1" class="com.study.spring.pojo.Dog"/>
<bean id="people" class="com.study.spring.pojo.People"/>
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;

小结:

@Resource:Java 注解

小结:

八、使用注解开发

环境配置:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>   
</beans>

8.1 Bean 的实现

<!--指定注解扫描包-->
<context:component-scan base-package="com.study.spring"/>
/*
    @Component:组件
    相当于配置文件中 <bean id="user" class="当前注解的类"/>
    可以指定对象名:@Component("对象名"),默认:类名首字母小写
 */
@Component
public class User {
    public String name;
}

8.2 属性注入

@Component
public class User {
    // 相当于配置文件中 <property name="name" value="测试"/>
    @Value("测试")
    public String name;
}
@Value("测试")
public void setName(String name) {
    this.name = name;
}
@Test
public void testUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user");
    System.out.println(user.name);
}

8.3 衍生注解

8.4 自动装配注解

8.5 作用域

@Component
@Scope("prototype")
public class User {
    public String name;
}

小结:

<!--指定注解扫描包-->
<context:component-scan base-package="com.study.spring"/>
<context:annotation-config/>

九、使用 Java 的方式配置 Spring

搭建环境

// @Component:将这个类,标注为Spring的一个组件,放到容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
/*
    @Configuration:
        代表这是一个配置类,等同于beans.xml
        本身就是一个组件(@Component),
        也会被Spring容器托管,注册到容器中
 */
@Configuration
// 可以设置扫描包
@ComponentScan("com.study.spring.config")
// 可以引入其它配置类
@Import(MyConfig2.class)
public class MyConfig {

    /*
        注册一个bean,相当于bean标签
        方法名:相当于bean标签中的id属性
        方法的返回值:相当于bean标签中的class属性
     */
    @Bean
    public User getUser() {
        // 返回要注入到bean的对象
        return new User();
    }
}
@Test
public void testUser() {
    // 使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
    // getBean的参数为,@Bean对应的方法名,非类名首字母小写!!
    User user = (User) context.getBean("getUser");
    System.out.println(user.getName());
}

十、代理模式

10.1 静态代理

代码实现

// 租房
public interface Rent {
    public void rent();
}
// 房东:实现租房接口
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房屋...");
    }
}
// 代理:代理房东、实现租房接口、并增加附属操作
public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

    // 有参构造方式,注入真实对象
    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        seeHouse();
        // 房东租房
        host.rent();
        hetong();
        fare();
    }

    // 看房
    public void seeHouse() {
        System.out.println("中介带看房");
    }

    // 签合同
    public void hetong() {
        System.out.println("签订合同");
    }

    // 收中介费
    public void fare() {
        System.out.println("收中介费");
    }
}
// 客户
public class Client {
    public static void main(String[] args) {
        // 房东要租房子
        Host host = new Host();
        // 代理:中介代理房东出租房子,但是,代理会增加附属操作
        Proxy proxy = new Proxy(host);
        // 客户不用面对房东,直接找中介即可
        proxy.rent();
    }
}

10.2 加深理解

静态代理实现 CRUD

// 抽象角色:增删改查业务
public interface UserService {
    public void add();

    public void delete();

    public void update();

    public void query();
}
// 真实对象:完成增删改查操作
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询用户");
    }
}
// 代理角色:增加日志的实现
public class UserServiceProxy implements UserService {
    public UserServiceImpl userService;

    // set方法,注入真实对象(注入方式:有参构造、set方法)
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    // 日志方法
    public void log(String msg) {
        System.out.println("[Debug] 使用了" + msg + " 方法");
    }
}
@Test
public void testUser() {
    // 真实业务
    UserServiceImpl userService = new UserServiceImpl();
    // 代理类
    UserServiceProxy proxy = new UserServiceProxy();
    // 使用代理类,增加了日志功能的实现
    proxy.setUserService(userService);
    proxy.add();
    proxy.delete();
    proxy.update();
    proxy.query();
}

10.3 动态代理

InvocationHandler

/*
    处理代理实例,并返回结果
    参数:
    proxy:代理类,代理的真实代理对象;
    method:要调用某个对象真实的方法的Method对象;
    args:代理对象方法传递的参数;
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = method.invoke(target, args);
    return result;    
}

Proxy

代码实现

// 用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 生成得到代理类(固定代码)
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    @Override
    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 动态代理的本质,就是使用反射机制实现
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    // 日志方法
    public void log(String msg) {
        System.out.println("[Debug] 使用了" + msg + " 方法");
    }
}
public class Client {
    public static void main(String[] args) {
        // 真实角色
        UserServiceImpl userService = new UserServiceImpl();
        // 代理角色,不存在,需要动态创建
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        // 设置要代理的对象
        pih.setTarget(userService);
        // 动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}

动态代理的好处

十一、AOP

11.1 什么是 AOP

11.2 Aop 在 Spring 中的作用

11.3 使用 Spring 实现 Aop

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>    
</dependency>

第一种方式:通过 Spring API 实现

public interface UserService {
    public void add();

    public void delete();

    public void update();

    public void select();
}
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void select() {
        System.out.println("查询用户");
    }
}
public class Log implements MethodBeforeAdvice {
    /*
        method:要执行的目标对象的方法
        args:参数
        target:目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
    }
}
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        // returnValue:返回值
        System.out.println("执行了" + method.getName() + "方法,返回值:" + returnValue);
    }
}
<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.study.spring.service.UserServiceImpl"/>
    <bean id="log" class="com.study.spring.log.Log"/>
    <bean id="afterLog" class="com.study.spring.log.AfterLog"/>

    <!--方式一:使用原生Spring API接口-->
    <!--配置AOP,需要导入Aop的约束-->
    <aop:config>
        <!--切入点:expression:表达式,execution(要执行的位置:修饰符 返回值 类名 方法名 参数 )-->
        <aop:pointcut id="pointcut" expression="execution(* com.study.spring.service.UserServiceImpl.*(..))"/>

        <!--advisor:环绕增加 advice-ref:对应增加方法的bean pointcut-ref:切入点-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
@Test
public void testUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 增加 UserService.class 后,不再需要强制转换
    // 注意点:动态代理,代理的是接口,不是实现类
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
    userService.delete();
    userService.update();
    userService.select();
}

第二种方式:自定义类(切面),实现 Aop

// 自定义切面(需要插入的类)
public class DiyPointCut {
    public void before() {
        System.out.println("=========方法执行前=========");
    }

    public void after() {
        System.out.println("=========方法执行后=========");
    }
}
<!--方式二:自定义类(切面),实现 Aop-->
<!--注册bena-->
<bean id="diy" class="com.study.spring.diy.DiyPointCut"/>

<aop:config>
    <!--自定义切面,ref:要引用的类-->
    <aop:aspect ref="diy">
        <!--切入点:-->
        <aop:pointcut id="point" expression="execution(* com.study.spring.service.UserServiceImpl.*(..))"/>
        <!--通知-->
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

切入点位置表达式

execution(* com.service ..*.*(..))
符号 含义
execution() 执行,表达式的主体
第一个 * 表示返回值的类型任意
com.service AOP 所切入的服务的包名,业务部分
包名后面的 .. 表示当前包及子包
第二个 * 表示类名,即所有类
.*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型

第三种方式:使用注解实现

// @Aspect:标注这个类是一个切面
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.study.spring.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("=========方法执行前=========");
    }

    @After("execution(* com.study.spring.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("=========方法执行后=========");
    }

    // 环绕增强:可以给定义一个参数,代表要获取处理切入的点
    @Around("execution(* com.study.spring.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        // getSignature():方法签名,查看目标方法名
        System.out.println(jp.getSignature());
        // 执行目标方法
        Object proceed = jp.proceed();
        System.out.println(proceed);
        System.out.println("环绕后");
    }
}
<!--方式三:使用注解实现-->
<bean id="annotationPointCut" class="com.study.spring.diy.AnnotationPointCut"/>

<!--
    开启注解支持:两种方式都可以实现
    JDK(默认:proxy-target-class="false")
    cglib:(proxy-target-class="true")
-->
<aop:aspectj-autoproxy/>

十二、整合 Mybatis

12.1 搭建环境

导入相关依赖

<!-- junit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>
<!--spring相关-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.17</version>
</dependency>
<!--Java 注解:JDK11 以上需要-->
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>
<!--mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>
<!--mybatis-spring 重点-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>
<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
<!--spring-jdbc-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.17</version>
</dependency>
<!--AOP 织入-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>
</dependency>
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

12.2 MyBatis 项目回顾

public class User {
    private int id;
    private String name;
    private String pwd;
    // get、set、toString
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <typeAliases>
        <package name="com.study.mybatis.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--注册 Mapper.xml-->
    <mappers>
        <mapper class="com.study.mybatis.mapper.UserMapper"/>
    </mappers>
</configuration>
public interface UserMapper {
    public List<User> selectUser();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mybatis.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from user;
    </select>
</mapper>
@Test
public void testUser() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sqlSessionFactory.openSession(true);

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.selectUser();
    for (User user : userList) {
        System.out.println(user);
    }

    sqlSession.close();
}

12.3 MyBatis-Spring

<!--mybatis-spring 重点-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>

整合实现方式一:SqlSessionTemplate

<!--Spring配合MyBatis,一般在MyBatis设置别名和set,其它在spring中设置-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
    <package name="com.study.mybatis.pojo"/>
</typeAliases>
<?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">

    <!--DataSource:使用Spring的数据源替换Mybatis的配置,如:c3p0 dbcp druid
        这里使用Spring-Jdbc:DriverManagerDataSource
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--SqlSessionFactory(固定格式)-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/study/mybatis/mapper/*.xml"/>
    </bean>

    <!--SqlSessionTemplate:SQLSession(固定格式)-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入sqlSessionFactory,因为没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>
public class UserMapperImpl implements UserMapper {
    // 以前使用SqlSession操作,现在都使用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    // 在接口实现类中,实现以前测试类的方法,并将方法值返回(使用时在spring中直接调用方法即可)
    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
<?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">

    <import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.study.mybatis.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
@Test
public void testSpringMybatis() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}

整合实现方式二:继承 SqlSessionDaoSupport

代码实现:

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        // getSqlSession()直接获取,不需要创建
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}
<!--SqlSessionDaoSupport实现,只需要sqlSessionFactory-->
<bean id="userMapper2" class="com.study.mybatis.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
@Test
public void testSpringMybatis2() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}

十三、声明式事务

13.1 回顾事务

未开启事务测试:

// 增加用户
public int addUser(User user);
// 删除用户
public int deleteUser(int id);
<insert id="addUser" parameterType="User">
    insert into user (id, name, pwd)
    values (#{id}, #{name}, #{pwd});
</insert>

<delete id="deleteUser" parameterType="int">
    <!--deletes 写错,模拟程序出错-->
    deletes from user where id = #{id};
</delete>
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        User user = new User(4, "小王", "123");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        mapper.addUser(user);
        mapper.deleteUser(1);
        return mapper.selectUser();
    }

    @Override
    public int addUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).addUser(user);
    }

    @Override
    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}
@Test
public void testSpringMybatis() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}

小结:

13.2 Spring 中的事务管理

spring 七种事务类型

事务类型 说明
REQUIRED 默认)支持当前事务,无事务,另起新事物
SUPPORTS 支持当前事务,无事务,以非事务执行
MANDATORY 以事务方式执行,无事务,抛异常
REQUIRES_NEW 新建事务,若有旧事务,挂起
NOT_SUPPORTED 不支持事务,如有事务,挂起
NEVER 以非事务执行,有事务,抛异常
NESTED 内切事务

配置 spring-dao.xml 文件

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给哪些方法配置事务-->
    <!--配置事务的传播特性,propagation:传播-->
    <tx:attributes>
        <tx:method name="insert" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.study.mybatis.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

小结:

上一篇下一篇

猜你喜欢

热点阅读