Spring

Spring

Spring简介

Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。

优点

  • Spring是一个开源免费的框架
  • 提供控制反转(IOC)和面向切面编程(AOP)
  • 轻量级、非入侵
  • 支持事务处理,支持框架整合

总结:Spring是一个轻量级的控制反转(IOC)的面向切面编程(AOP)的框架

官方下载地址https://repo.spring.io/release/org/springframework/spring/

帮助文档:https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/spring-framework-reference/html/

依赖:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.7</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.7</version>
</dependency>

七大组件

Spring家族

现代的Java的Java开发,就是基于Spring的开发

  • Spring Boot
    • 一个快速开发微服务的脚手架
    • 基于Spring Boot可以快速开发单个微服务
  • Spring Cloud
    • Spring Cloud 是基于Spring Boot实现的

现在大多数互联网公司使用Spring Boot进行快速开发,而掌握Spring Boot的前提是学好Spring 以及Spring MVC,这两者起到承上启下的作用。

IOC理论推导

IOC(Inversion of Control):控制反转

  • UserDao接口
public interface UserDao {
    void getUser();
}
  • UserDaoImpl实现类
public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("调用了默认的方法!");
    }
}
  • UserDaoMysqlImpl实现类
public class UserDaoMysqlImpl implements UserDao {
    public void getUser() {
        System.out.println("调用了mysql的方法!");
    }
}
  • UserService接口
public interface UserService {
    void getUser();
}
  • UserServiceImpl实现类
public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    public void getUser() {
        userDao.getUser();
    }
}
  • 测试类
@Test
    public void myTest01() {
        UserService userService = new UserServiceImpl();
        userService.getUser();
    }

案例分析

UserServiceImpl实现类会维持dao层的UserDao的引用,但是如果不进行设置注入的话,只是很僵硬的new一个dao层的某个具体实现类,在将来用户需要调用利用Service调用dao层的其他方法时,就得去重新写UserService的实现类,或者在原来的类中重新new一个对象注入,无论是重新写,还是在原来的基础上去new,都是不符合规范的,前者会导致过多的类出现,后者违背了开闭原则。

为此,需要进行一点小小的改动。。。

只需要在UserServiceImpl实现类中维持UserDao的引用,并提供set方法就行了。

重新编写测试类:

@Test
public void myTest01() {
    UserService userService = new UserServiceImpl();
    ((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
    userService.getUser();
    ((UserServiceImpl) userService).setUserDao(new UserDaoOracleImpl());
    userService.getUser();
}

小小的set方法带来的影响是巨大的!

对象的控制权已经从程序手中移交到用户手中,由原来干巴巴的创建一个具体的对象,变成了用户可以根据自己的需求注入不同的对象,这种通过set方法是实现控制反转的方式之一——依赖注入DI

Hello,Spring!

  • 配置环境
<!--新建Maven项目,导入依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.7</version>
</dependency>
  • pojo.HelloWorld
public class HelloWorld {
    private String str;
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
    @Override
    public String toString() {
        return "HelloWorld{" +
            "str='" + str + '\'' +
            '}';
    }
}
  • beans.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	
    <bean id="helloWorld" class="com.bin.pojo.HelloWorld">
        <!--如果该属性是非String的引用数据类型,则可以为bean的id-->
        <property name="str" value="你好,世界!" />
    </bean>

</beans>
  • 测试程序
@Test
public void myTest02() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    //获取相应的对象
    UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
    userServiceImpl.getUser();
}

创建对象的任务完全托付给了Spring容器,因此,要实现不同的操作,我们只需要修改对应的配置文件即可,这便是控制反转

IOC是一种编程思想:由主动编程变成被动的接收,

可以通过ClassPathXMlApplicationContext去浏览底层源码。

Spring的IOC:对象由Spring容器来创建,管理,装配!

IOC创建对象的方式

set注入

  • pojo.UserBySet
public class UserBySet {
    private String name;

    public UserBySet() {
        System.out.println("UserBySet对象被创建啦!");
    }
}
  • beans.xml
<bean id="userBySet" class="com.bin.pojo.UserBySet">
    <property name="name" value="小洪"/>
</bean>

构造注入

  • UserByConstructor
public class UserByConstructor {
    private String name;

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

    @Override
    public String toString() {
        return "UserByConstructor{" +
            "name='" + name + '\'' +
            '}';
    }
}
  • beans.xml
<bean id="userByConstructor" class="com.bin.pojo.UserByConstructor">
    <!--方式一,根据参数列表的下标设置value-->
    <!--<constructor-arg index="0" value="小斌"/>-->
    
    <!--方式二,根据参数名设置value-->
    <constructor-arg name="name" value="小学"/>
</bean>

注意

  • 使用set注入时,注意无参构造不能被覆盖掉。
  • 当new一个ClassPathXmlApplicationContext("beans.xml")时,就会把该文件下所有的bean创建出来。

依赖注入

构造器注入

详见IOC创建对象的方式。。。

Set注入【重点】

  • Address
public class Address {
    private String province;
    private String city;
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    @Override
    public String toString() {
        return "Address{" +
            "province='" + province + '\'' +
            ", city='" + city + '\'' +
            '}';
    }
}
  • Student
public class Student {
    private String name;
    private Address address;
    private String[] hobbies;
    private Map<String,String> subjects;
    private List<String> roommates;
    private Set<String> games;
    private Properties parents;
    private String girlfriend;
	//get、set、toString方法已省略
}
  • 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--引用bean-->
    <bean id="address" class="com.bin.Address">
        <property name="city" value="广州"/>
        <property name="province" value="广东"/>
    </bean>
	
    <bean id="student" class="com.bin.Student">
        <!--普通类型-->
        <property name="name" value="小斌"/>
        <!--引用类型-->
        <property name="address" ref="address"/>
        <!--数组-->
        <property name="hobbies">
            <array>
                <value>看电视</value>
                <value>写代码</value>
                <value>看小姐姐</value>
            </array>
        </property>
        <!--Set集合-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
                <value>CSGO</value>
            </set>
        </property>
		<!--Properties集合-->
        <property name="parents">
            <props>
                <prop key="father">大斌</prop>
                <prop key="mother">大红</prop>
            </props>
        </property>
        <!--List集合-->
        <property name="roommates">
            <list>
                <value>小雨</value>
                <value>小熊</value>
                <value>小明</value>
            </list>
        </property>
        <!--Map集合-->
        <property name="subjects">
            <map>
                <entry key="语文" value="11"/>
                <entry key="数学" value="99"/>
                <entry key="英语" value="88"/>
            </map>
        </property>
        <!--null值-->
        <property name="girlfriend">
            <null/>
        </property>
    </bean>
</beans>

注意

  • null值和空串是两个概念

拓展注入

使用以下两种命名空间的前提是导入xml约束。

c命名空间注入

c命名空间对应着构造器参数列表的命名空间,因此使用c命名空间注入的前提是存在有参构造。

p命名空间注入

p命名空间对应着类中的属性,p--->property,使用p命名空间注入的前提是属性存在set方法。

  • User
public class User {
    private String name;
    private int age;
    public User() {
    }
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • applicationContext.xml
<!--在beans的属性值中添加以下两项-->
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

<!--c命名空间注入-->
<bean id="user1" class="com.bin.User" c:age="18" c:name="小斌"/>

<!--p命名空间注入-->
<bean id="user2" class="com.bin.User" p:age="22" p:name="小明"/>

Bean的作用域

bean标签有scope属性,该属性代表了bean的作用域。

  • singleton(单例模式):

全局只有一个bean的对象,无论调用getBean几次,都是指向该对象的地址。

  • prototype(原型模式):

全局可以有多个bean的对象,每调用一次getBean,都会创建一个新的对象。

request、session、application在Web开发中会用到。

Bean的装配方式

自动装配方式的适用性,对于非String的引用数据类型属性的装配。

对于基本数据类型属性,通过手动装配。

1.手动装配

<bean id="dog" class="com.bin.pojo.Dog"/>
<bean id="cat" class="com.bin.pojo.Cat"/>

<bean id="people" class="com.bin.pojo.People" scope="prototype">
    <property name="name" value="小斌"/>
    <property name="cat" ref="cat"/>
    <property name="dog" ref="dog"/>
</bean>

2.通过bean标签的autowire属性实现自动装配

  • byType:会自动搜寻上下文,搜寻与属性类型一致的bean

  • byName:会自动搜寻上下文,搜寻与属性名一致的bean

注意:

  • 采用byType属性值进行自动装配时,若上下文存在两个类型一致的bean,则会报异常
  • 采用byName属性值进行自动装配时,若上下文不存在与该属性名一致的bean,会报异常

3.通过注解实现自动装配重点

在applicationContext文件中添加xml约束、添加注解支持--->在对应的实体类属性上添加@Autowired注解

  • 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: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/>
    <bean id="dog" class="com.bin.pojo.Dog"/>
    <bean id="cat" class="com.bin.pojo.Cat"/>
    <bean id="people" class="com.bin.pojo.People" /> 
</beans>
  • People
public class People {
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;

    private String name;

    public Dog getDog() {
        return dog;
    }
    public Cat getCat() {
        return cat;
    }
    public String getName() {
        return name;
    }
    @Override
    public String toString() {
        return "People{" +
            "dog=" + dog +
            ", cat=" + cat +
            ", name='" + name + '\'' +
            '}';
    }
}

注意

  • 该注解会同时采用byName方式和byType方式寻找合适的bean,如果byName找不到,再采用byType方式。
  • 如果两种方式都找不到,比如存在多个id值且这些id值与属性值都不一致的情况,可以通过添加@Qualifier注解进行锁定
@Qualifier(value = "dog2")
private Dog dog;
  • @Autowired注解的属性:required:boolean,true:该属性不能为null,false:该属性可以为null
  • 不熟悉注解属性的,可以点进去注解源码中了解。

使用注解开发

在Spring 4之后,使用注解开发需要导入aop的依赖包,此外还要添加context的xml约束和对注解的支持。

而spring-webmvc依赖就已经包含spring-aop的包了。

使用步骤

导入spring-aop依赖--->在applicationContext.xml文件添加context的xml约束和指定扫描的包--->测试

  • 导入spring-aop依赖
  • 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: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:component-scan base-package="com.bin"/>
</beans>
  • 测试
@Test
public void test01() {
    //注意这里传参是传扫描的包名了。。。
    ApplicationContext context = new AnnotationConfigApplicationContext("com.bin");
    User user = context.getBean("user", User.class);
    System.out.println(user);
}

bean

  • @Component
@Service
public class UserService {
    @Override
    public String toString() {
        return "UserService{娘子我欠你太多}";
    }
}
//该注解出现在类上,等价于<bean id="userService" class="com.bin.service.UserService">
  • @Repository:用于dao层
  • @Service:用于service层
  • @Controller:用于controller层

以上四种注解注解在类上,它们功能完全一致,只是MVC分层起到标识作用。

属性注入

  • @Value("小斌")
@Component
public class User {
    @Value("张三") //等价于<property name="name" value="zhangsan"/>
    private String name;

    @Override
    public String toString() {
        return "User{" +
            "name='" + name + '\'' +
            '}';
    }
}

对于复杂数据类型List、Map、Set...还是用xml文件注入

自动装配

  • @Autowired,通过上下文查找符合与属性名或属性的类型相同的bean,如果存在多个这样的bean无法锁定,可以通过添加另一个注解@Qualified(value="cat2")来确定该bean
  • @Nullable:被标记该注解的字段,允许为空
  • @Resource,与@Autowired用法相似

作用域

  • @Scope("singleton"):单例模式
  • @Scope("prototype"):原型模式
  • @Scope("request")...

小结

注解?XML?

最佳用法应该是:xml用来管理bean,注解用来属性注入。

再见!xml文件

这是一个小拓展,完全脱离xml文件

项目结构图

  • JavaConfig
@ComponentScan("com.bin") //扫描包下的注解
@Configuration  //代表这是一个配置类
public class JavaConfig {
    @Bean
    public MyController getMyController() {
        return new MyController();
    }

    @Bean
    public UserDao getUserDao() {
        return new UserDao();
    }

    @Bean
    public UserService getUserService() {
        return new UserService();
    }

    @Bean
    public User getUser() {
        return new User();
    }
    
}
  • User
@Component
public class User {
}
  • UserDao
@Repository
public class UserDao {
}
  • UserService
@Service
public class UserService {
}
  • MyController
@Controller
public class MyController {
}
  • 测试类
@Test
public void test01() {
    ApplicationContext context = new AnnotationConfigApplicationContext("com.bin");
    MyController myController = context.getBean("myController", MyController.class);
    User user = context.getBean("user", User.class);
    UserDao userDao = context.getBean("userDao", UserDao.class);
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println(myController);
    System.out.println(user);
    System.out.println(userDao);
    System.out.println(userService);
}

小结

脱离了xml文件,需要新建一个Config类来代替xml。

代理模式

为什么学习代理模式?

  • Spring AOP的底层就是用代理模式实现的。
  • 代理模式分类:静态代理模式,动态代理模式

角色分类

  • 抽象角色:一般用接口来表示
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,会另外做一些事情
  • 客户:访问代理对象的人

静态代理模式

  • 抽象角色
public interface Rent {
    void rent();
}
  • 真实角色
//房东
public class Host implements Rent {
    public void rent() {
        System.out.println("出租房子!");
    }
}
  • 代理角色
//中介
public class Proxy implements Rent {
    private Rent host;

    public Proxy() {
    }

    public Proxy(Rent host) {
        this.host = host;
    }

    public void rent() {
        regist();
        host.rent();
        charge();
        constant();
    }
    public void regist() {
        System.out.println("注册房源信息!");
    }

    public void charge() {
        System.out.println("收中介费!");
    }

    public void constant() {
        System.out.println("签合同!");
    }
}
  • 客户
public class Client {
    public static void main(String[] args) {
        Rent host = new Host();
        Rent proxy = new Proxy(host);
        proxy.rent();
    }
}

小结

三要素

  • 客户找代理角色
  • 代理角色和真实角色都有类似的目的(实现了相同的接口)
  • 代理角色会比真实角色做更多的事情

优点

  • 可以让真实角色的操作更加纯粹,不用关心一些公共业务
  • 公共业务交给代理角色去做,实现业务分工
  • 公共业务发生拓展时,方便管理。

缺点

  • 一个真实角色就会产生一个代理角色,代码量会加倍,开发效率会变低。

动态代理

一个真实角色就会产生一个代理角色,这是静态代理模式致命的缺点,那为了克服这个弱点,可以将代理角色抽象出来,让这个代理角色能代理许多真实角色,动态代理应运而生。

具体代码

  • UserService
public interface UserService {
    void insert();
    void delete();
    void select();
    void update();
}
  • UserServiceImpl
public class UserServiceImpl implements UserService {
    public void insert() {
        System.out.println("插入一条数据");
    }

    public void delete() {
        System.out.println("删除一条数据");
    }

    public void select() {
        System.out.println("查询一条数据");
    }

    public void update() {
        System.out.println("修改一条数据");
    }
}
  • ProxyInvokeHandler
//自动生成代理类
public class ProxyInvokeHandler implements InvocationHandler {
    private Object target;

    public void setObject(Object object) {
        this.target = object;
    }
	//获取代理类
    public Object getProxy() {
         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
	
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        //调用真实对象的方法并返回
        return method.invoke(target, args);
    }
	//代理角色附加的方法
    public void log(String methodName){
        System.out.println("[Debug]---->调用了" + methodName +"方法");
    }
}
  • Client
public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyInvokeHandler handler = new ProxyInvokeHandler();
        //只要将真实对象注入该类中即可!
        handler.setObject(userService);
        UserService proxy = (UserService) handler.getProxy();
        proxy.insert();
        proxy.delete();
        proxy.select();
        proxy.update();
    }
}

小结

  • 动态代理相对静态代理而言是一次质的提升!
  • 动态代理的实现利用的是反射机制。

AOP

AOP:面向切面编程,通过预编译的方式和运行期间动态代理实现程序功能统一维护的一项技术。

应用对象与AOP

应用对象只是实现它们应该做的——实现业务逻辑,仅此而已,它们并不负责其他系统级的关注点,比如说日志和事务支持。

引入AOP就显得非常重要。

AOP的主要功能

日志记录,性能统计,安全控制,事务处理,异常处理等

  • 横切关注点:跨越应用程序多个模块的程序或功能,与业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,比如:安全,日志,缓存,事务等。

  • 切面(Aspect):横切关注点被模块化后的对象,通常是一个类。

  • 通知(Advice):切面需要完成的工作,通常是一个方法。

  • 目标(Target):被通知的对象。

  • 代理(Proxy):向目标通知后创建的对象。

  • 切入点(PointCut):切面通知应该通知到的一个“地点”。

  • 连接点(JointPoint):与切入点匹配的执行点。

Spring实现AOP

  • 环境准备

    • 导入依赖
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.6</version>
    </dependency>
    
    • 在提供xml约束
    <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">
    </beans>
    

方式一

使用Spring提供的API接口

  • UserService
public interface UserService {
    void insert();
    void delete();
    void select();
    void update();
}
  • UserServiceImpl
public class UserServiceImpl implements UserService {
    public void insert() {
        System.out.println("增加了一个用户");
    }

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

    }

    public void select() {
        System.out.println("查询了一个用户");

    }

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

    }
}
  • BeforeLog
public class BeforeLog implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName() + "类将要执行" + method.getName() + "方法!" );
    }
}
  • AfterLog
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(o1.getClass().getName() + "类执行了" + method.getName() + "方法!");
    }
}
  • 在配置文件中配置bean,以及AOP相关的一些配置
<bean id="userServiceImpl" class="com.bin.service.UserServiceImpl"/>
<bean id="afterLog" class="com.bin.log.AfterLog"/>
<bean id="beforeLog" class="com.bin.log.BeforeLog"/>

<aop:config>
    <!--id:切入点的id,expression:通过execution表达式确定具体切入的位置
		参数1:返回类型,参数2:具体类的方法,此处是所有方法,(..)代表任意参数-->
    <aop:pointcut id="pointcut1" expression="execution(* com.bin.service.UserServiceImpl.*(..))"/>
    <!--环绕信息-->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut1"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut1"/>
</aop:config>

执行结果

方式二

自定义实现AOP

  • DiyLog
public class DiyLog {
    public void before() {
        System.out.println("=======Before========");
    }

    public void after() {
        System.out.println("=========After===========");
    }
}
  • applicationContext.xml
<bean id="userServiceImpl" class="com.bin.service.UserServiceImpl"/>
<bean id="diyLog" class="com.bin.diy.DiyLog"/>
<aop:config>
    <!--aspect:切面,是具体的一个bean-->
    <aop:aspect id="aspect" ref="diyLog">
        <aop:pointcut id="point" expression="execution(* com.bin.service.UserServiceImpl.*(..))"/>
        <!--method:通知,切面中的具体的一个方法-->
        <aop:after method="after" pointcut-ref="point"/>
        <aop:before method="before" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

运行结果

方式三

利用注解

  • AnooLog
@Aspect
public class AnooLog {

    @Before("execution(* com.bin.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("=======前========");
    }
    @After("execution(* com.bin.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("========后=======");
    }
}
  • applicationContext.xml
<!--别忘了注册bean,老是忘了-->
<bean id="anooLog" class="com.bin.anno.AnooLog"/>
<!--提供注解支持-->
<aop:aspectj-autoproxy />

运行结果

Spring整合MyBatis

回忆MyBatis

  • 配置环境

    • 导入依赖
    <dependencies>
        <dependency>
            <!--Java连接数据库支持-->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <!--Mybatis框架依赖-->
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
    </dependencies>
    
    • 配置资源导出路径
    <!--资源导出路径,不配置的话,在用流的时候可能找不到资源文件-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
    
  • 编写实体类

public class User {
    private int id;
    private String username;
    private String pwd;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  • 编写核心配置文件
<?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>
    <typeAliases>
        <package name="com.bin.pojo"/>
    </typeAliases>
    <environments default="development">
        <!--环境名-->
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--驱动类-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--URL-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false" />
                <!--用户名-->
                <property name="username" value="root"/>
                <!--密码-->
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.bin.dao.UserMapper"/>
    </mappers>
</configuration>
  • 编写接口
public interface UserMapper {
    List<User> getUsers();
}
  • 编写接口对应的Mapper文件
<?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.bin.dao.UserMapper">
    <select id="getUsers" resultType="user">
        select * from user
    </select>

</mapper>
  • 在核心配置文件中注册mapper
<mappers>
    <mapper class="com.bin.dao.UserMapper"/>
</mappers>
  • 测试
public void test01() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = factory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

Spring整合MyBatis

在回忆完MyBatis的基础上,进行整合。

方式一

  • 导入依赖

  • 新建spring-dao.xml文件

    • 编写数据源
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--这是原来在mybatis核心配置文件需要配置的东西,我们交给Spring来做-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    • 编写SqlSessionFactory对应的bean
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    
    • 编写SqlSession对应的bean(SqlSessionTemplate)
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--该类无set方法,只能通过构造器注入-->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
  • 编写Mapper接口的实现类

public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSession;
    //set注入
    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    public List<User> getUsers() {
        return sqlSession.getMapper(UserMapper.class).getUsers();
    }
}
  • 注册到applicationContext.xml中
<!--导入spring-dao.xml分工会更加明显-->
<import resource="spring-dao.xml"/>
<bean id="userMapperImpl" class="com.bin.dao.UserMapperImpl">
    <!--记得依赖注入!找了很久的bug竟然是在这里-->
    <property name="sqlSession" ref="sqlSession"/>
</bean>
  • 测试
@Test
public void test02() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper mapper = context.getBean("userMapperImpl", UserMapper.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
}

方式二

  • 编写spring-dao.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="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <!--配到工厂这里就行了-->
</beans>
  • UserMapperImpl2
//继承SqlSessionDaoSupport类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    public List<User> getUsers() {
        return getSqlSession().getMapper(UserMapper.class).getUsers();
    }
}
  • 注册bean
<bean id="userMapperImpl2" class="com.bin.dao.UserMapperImpl2">
    <!--依赖注入-->
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
  • 测试
@Test
public void test03() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper mapper = context.getBean("userMapperImpl2", UserMapper.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
}

事务

回顾事务

事务:一组业务逻辑,要么都成功要么都失败。

四大特性(ACID):

  • 原子性
  • 隔离性
  • 一致性
  • 持久性

声明式事务

原理:AOP

测试

User

package com.bin.pojo;
public class User {
    private int id;
    private String username;
    private String pwd;
    public User(int id, String username, String pwd) {
        this.id = id;
        this.username = username;
        this.pwd = pwd;
    }

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}
  • UserMapper
public interface UserMapper {

    List<User> getUsers();

    int deleteUser(@Param("id") int id);

    int insertUser(User user);
}
  • UserMapper.xml
<mapper namespace="com.bin.dao.UserMapper">
    <select id="getUsers" resultType="user">
        select * from user
    </select>

    <delete id="deleteUser">
        delete from user where id = #{id}
    </delete>

    <insert id="insertUser" parameterType="user">
        insert into user (id,username,pwd) values (#{id},#{username},#{pwd})
    </insert>

</mapper>
  • UserMapperImpl
public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSession;

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

    public List<User> getUsers() {
        deleteUser(2);
        insertUser(new User(5,"大斌","1jljlkkl"));
        return sqlSession.getMapper(UserMapper.class).getUsers();
    }

    public int deleteUser(int id) {
        return sqlSession.getMapper(UserMapper.class).deleteUser(id);
    }

    public int insertUser(User user) {
        return sqlSession.getMapper(UserMapper.class).insertUser(user);
    }
}
  • 将UserMapperImpl注册到applicationContext.xml中
<bean id="userMapperImpl" class="com.bin.dao.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>
  • 在spring-dao.xml中开启事务支持

    • 添加aop和tx的xml约束
    • 注册DataResourceTransactionManager的bean
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    • 声明通知
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--通知的范围 对所有方法都开启事务-->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
    • 声明切面和环绕
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.bin.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
    
posted @ 2021-06-08 22:21  Code_Ice  阅读(76)  评论(0)    收藏  举报