Spring5

Spring

  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架

下载地址:GitHub - spring-projects/spring-framework: Spring Framework

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

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
   
 <!--和Mybatis整合用到的依赖-->   <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.19</version>
</dependency>

优点

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

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

组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z92GKaE1-1659085274165)(D:\桌面\javaPicture\1651636900175.png)]

拓展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CtlqrbRD-1659085274166)(D:\桌面\javaPicture\1651637170105.png)]

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

IOC理论推导

在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序代码量十分大,修改一次的成本代价十分昂贵!

我们使用一个Set接口实现,已经发生了革命性的变化

private UserDao userDao;

public void setUser(UserDao userDao){
    this.userDao=userDao;
}
  • 之前,程序是主动创建对象 控制权在程序员手上
  • 使用set注入后程序不在具有主动性,而是被动接收对象
  • 这种思想从本质上解决了问题,我们程序员不用再去管理对象的创建了,系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC思想

IOC本质

IOC是一种设计思想,DI(依赖注入)是实现Ioc的一种方法,也有人认为DI只是IOC的另一种说法,没有IOC的程序中,我们面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式变了.

控制反转就是一种通过描述(XML或注解) 并通过第三方去生产获取特定对象的方式 在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入

Hello Spring

package com.dyz.pojo;

public class Hello {
    private String str;

    public Hello(String str) {
        this.str = str;
    }
    public Hello() {
    }


    public String getStr() {
        return str;
    }

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

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

Spring容器 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">
<!--   类型 变量名 =new 类型()
        id=变量名   class=new 的对象  property给对象中的属性赋值    -->
    <bean id="hello" class="com.dyz.pojo.Hello" >
        <property name="str" value="Spring"/>
    </bean>

</beans>

UserDao接口

package com.dyz.dao;

public interface UserDao {
   void getUser();
}

UserDao实现类

package com.dyz.dao;

public class MysqlUserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("获取Mysql用户信息");
    }
}
package com.dyz.dao;

public class OracleUserDaoImpl implements  UserDao{
    @Override
    public void getUser() {
        System.out.println("获取Oracle用户数据");
    }
}

UserService接口

package com.dyz.Service;

public interface UserService {
    void getUser();
}

UserService实现类

package com.dyz.Service;

import com.dyz.dao.UserDao;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao){  //必须要有该set方法才能在Spring容器中设置该属性的值
        this.userDao=userDao;
    }
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

Spring容器 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">
    <!--   类型 变量名 =new 类型()
            id=变量名   class=new 的对象  property给对象中的属性赋值    -->
    <bean id="MysqlUser" class="com.dyz.dao.MysqlUserDaoImpl"/>
    <bean id="OracleUser" class="com.dyz.dao.OracleUserDaoImpl"/>
    <bean id="UserService" class="com.dyz.Service.UserServiceImpl">
<!--        ref是引用Spring容器中已经创建的对象 在UserServiceImpl中需要有对应的set方法
才可以设置userDao的值value为具体的值  基本数据类型-->
        <property name="userDao" ref="MysqlUser"/>
    </bean>
</beans>

Test

package com.dyz.Service;

import com.dyz.dao.MysqlUserDaoImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserDaoServiceTest {
    @Test
    public void getUser(){
        //拿到Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取Spring容器中创建的对象
        UserServiceImpl userDao = (UserServiceImpl) context.getBean("UserService");
        //执行该对象的方法
        userDao.getUser();

    }
}

IOC创建对象的方式

  1. 默认使用无参构造创建对象

    public User(){
            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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.dyz.pojo.User">
        <property name="name" value="大宇宙"/>
    </bean>
    
    </beans>
    
    public void User(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        context.getBean("user"); //进入了无参构造器  相当于实例化对象 默认进入了无参构造器
    }
    
  2. 假设使用有参构造创建对象

    2.1 使用参数下标

    public User(String name) {
            this.name = name;
    }
    
    <!--    第二种使用有参构造创建对象 下标赋值-->
        <bean id="user" class="com.dyz.pojo.User">
            <constructor-arg index="0" value="大宇宙2"/>
        </bean>
    

    2.2 使用参数类型 (不建议使用)

    <!--   第三种使用有参构造创建对象 通过参数类型给参数赋值  不建议使用-->
    <bean id="user" class="com.dyz.pojo.User">
        <constructor-arg type="java.lang.String" value="大宇宙3"/>
    </bean>
    

    2.3 使用参数名

    <!--    第四种使用有参构造创建对象 通过参数名给参数赋值  建议使用-->
        <bean id="user" class="com.dyz.pojo.User">
            <constructor-arg name="name" value="大宇宙4"/>
        </bean>
    
  3. 注:在一个Spring容器中一个对象被创建了之后其他bean中的对象也会一起被创建出来 即使你没有去获取该对象;

别名

    <bean id="user" class="com.dyz.pojo.User">
        <constructor-arg name="name" value="大宇宙4"/>
    </bean>
<alias name="user" alias="米西米西花不拉几"/>
public void User2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    User user = (User) context.getBean("米西米西花不拉几");
    user.show();
}

bean

<!--    id:bean的唯一标识符,也就是我们所说的对象名 可以起别名
        class:对象对应的类型,必须是全限定名 :包名+类名
        name:id的别名 而且name可以同时取多个别名-->
    <bean id="user" class="com.dyz.pojo.User" name="user2,u2">
        <constructor-arg name="name" value="大宇宙4"/>
    </bean>

Import

这个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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="beans.xml"/>
    <import resource="beans2"/>
    
</beans>

依赖注入

构造器注入

Set方式注入(重点)

  • 依赖注入:Set注入!
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性,由容器来注入!

环境搭建

  1. 复杂类型

    package com.dyz.pojo;
    
    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "Address{" +
                    "address='" + address + '\'' +
                    '}';
        }
    }
    
    
  2. 真实测试对象

    package com.dyz.pojo;
    
    import java.util.*;
    
    public class Student {
        private String name;
        private Address address;
        private  String[] books;
        private List<String> hobby;
        private Set<String> games;
        private Map<String,String> card;
        private String  wife;
        private Properties info;
    
        public Student() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    
        public String[] getBooks() {
            return books;
        }
    
        public void setBooks(String[] books) {
            this.books = books;
        }
    
        public List<String> getHobby() {
            return hobby;
        }
    
        public void setHobby(List<String> hobby) {
            this.hobby = hobby;
        }
    
        public Set<String> getGames() {
            return games;
        }
    
        public void setGames(Set<String> games) {
            this.games = games;
        }
    
        public Map<String, String> getCard() {
            return card;
        }
    
        public void setCard(Map<String, String> card) {
            this.card = card;
        }
    
        public String getWife() {
            return wife;
        }
    
        public void setWife(String wife) {
            this.wife = wife;
        }
    
        public Properties getInfo() {
            return info;
        }
    
        public void setInfo(Properties info) {
            this.info = info;
        }
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    ", books=" + Arrays.toString(books) +
                    ", hobby=" + hobby +
                    ", games=" + games +
                    ", card=" + card +
                    ", wife='" + wife + '\'' +
                    ", info=" + info +
                    '}';
        }
    }
    

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="address" class="com.dyz.pojo.Address">
        <property name="address" value="江苏省南京市"/>
    </bean>
    <bean id="student" class="com.dyz.pojo.Student">
        <!--    第一种普通值注入-->
        <property name="name" value="张宇"/>
        <!--        第二种 bean注入-->
        <property name="address" ref="address"/>
        <!-- 数组 -->
        <property name="books" >
          <array>
              <value>java</value>
              <value>大数据</value>
              <value>西游记</value>
          </array>
        </property>
        <!--集合-->
        <property name="hobby">
            <list>
                <value>游泳</value>
                <value>玩游戏</value>
                <value>听音乐</value>
            </list>
        </property>
        <!-- map-->
        <property name="card">
            <map>
                <entry key="身份证" value="23546546545466"/>
                <entry key="银行卡" value="132132156151"/>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
                <value>QQSpeed</value>
            </set>
        </property>
        <!--        空注入1 value=""或者null标签-->
        <property name="wife">
            <null></null>
        </property>
<!--        properties-->
        <property name="info">
            <props>
                <prop key="学号">2018150386</prop>
                <prop key="性别"></prop>
                <prop key="姓名">小米</prop>
            </props>
        </property>

    </bean>

</beans>

拓展方式注入

我们可以使用p命名和c命名空间注入

使用

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    p命名注入,可以直接注入属性的值-->
<bean id="user" class="com.dyz.pojo.User" p:name="张宇" p:age="22"/>
<!--c命名注入 通过有参构造注入属性的值-->
<bean id="user2" class="com.dyz.pojo.User" c:age="22" c:name="张宇2"/>

</beans>

测试

package com.dyz.pojo;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    @Test
    public void Student(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.toString());
    }
    @Test
    public void UserTest(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("UserBeans.xml");
        User user = Context.getBean("user2", User.class);//利用反射就可以不用强转自动转换类型
        System.out.println(user.toString());
    }
}

注意:两种命名方式需要加xml约束

       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

bean的作用域

  1. 单例模式(Spring默认机制)

    <bean id="user" class="com.dyz.pojo.User" p:name="张宇" p:age="22" scope="singleton"/>
    
    public void UserTest(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("UserBeans.xml");
        User user1 = Context.getBean("user2", User.class);//利用反射就可以不用强转自动转换类型
        User user2 = Context.getBean("user2", User.class);
        System.out.println(user1==user2);
    }//true
    
  2. 原型模式(每次从容器中get的时候都会产生一个新对象)

    <bean id="user" class="com.dyz.pojo.User" p:name="张宇" p:age="22" scope="prototype"/>
    
    public void UserTest(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("UserBeans.xml");
        User user1 = Context.getBean("user", User.class);//利用反射就可以不用强转自动转换类型
        User user2 = Context.getBean("user", User.class);
        System.out.println(user1==user2);
    }//false
    

    getbean同一个类得到对象的hashCode不同

  3. 其余的request session application这些只能在web开发中使用到!

Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种装配的方式

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配bean

ByName自动装配

<bean id="cat" class="com.dyz.pojo.Cat"/>
    <bean id="dog" class="com.dyz.pojo.Dog"/>
<!--    byName会自动在Spring容器上下文寻找与该类中Set方法后面名字相同的对象id并自动装配
          <property name="dog" ref="dog"/>
          <property name="cat" ref="cat"/>-->
    <bean id="person" class="com.dyz.pojo.Person" autowire="byName">

test

@Test
public void Test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Person person = context.getBean("person", Person.class);
    person.getCat().shout();
    person.getDog().shout();
}

ByType自动装配

<!--    byName会自动在Spring容器上下文寻找与该类中属性类型相同的beanid(类型看bean class)并自动装配
         该方法装配同一种类型的bean对象只能装配一个-->
    <bean id="person2" class="com.dyz.pojo.Person" autowire="byType"/>

小结:

  • byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
  • byType的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性类型一致

使用注解实现自动装配

要使用注解须知:

  1. 导入约束,context约束

  2. 配置注解的支持

    <?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

    直接在属性上或者set方法上使用即可

    使用Autowired我们可以不用编写set方法 前提是你这个自动装配的属性在IOC(Spring) 容器中存在,且符合名字byName

    package com.dyz.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class Person {
        String name;
        @Autowired //会自动匹配与beanid相同的对象实现自动装配
        Cat cat;
        @Autowired
        Dog dog;
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", cat=" + cat +
                    ", dog=" + dog +
                    '}';
        }
    
        public Person() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public Dog getDog() {
            return dog;
        }
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    }
    

    科普:

    @Nullable 字段标记了这个注解,说明这个字段可以为null
    
    
    public @interface Autowired {
        boolean required() default true;
    }
    

    测试代码

    String name;
    @Autowired //会自动匹配与beanClass相同的对象实现自动装配
    Cat cat;
    //如果定义了Autowired的required属性为false 说明这个对象可以为null 否则不允许为空
        @Autowired(required = false)
        @Qualifier(value = "dog2") //此处value为dog 他只会去找类型为Dog beanid=dog2的bean
        Dog dog;
    

如果@Autowired自动装配的对象有多个同类型的,可以通过 @Qualifier(value = “xxx”)去配置 @Autowired来确定具体装配的是哪一个bean,来指定唯一的bean对象注入

@Resource注解

    String name;
    @Resource(name = "cat1")
    Cat cat;

    @Resource(name = "dog2")
    Dog dog;

小结:

@Resource和@Autowired的区别

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过ByType方式实现,而且必须要求这个对象存在!
  • @Resource默认通过ByName的方式实现,如果找不到名字则通过byType实现!如果两个都找不到的情况下就报错
  • 执行顺序不同

使用注解开发

在Spring4之后,要使用注解开发必须要保证aop的包导入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IkC8Ibs5-1659085274168)(D:\桌面\javaPicture\1652161036555.png)]

使用注解需要导入context约束,增加注解支持

<?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.dyz.pojo"/>
</beans>
1. 当这两个配置同时配置时,<context-annotation-config />将失效,以<context:component-scan base-package="" />为准!   
2. @Component、@Controller、@Service、@Repository这些注解本身并不具有声明注册bean的功能,在没有<context:component-scan>扫描解析之前是没有任何作用的!


context-annotation-config:

**对@Component、@Controller、@Service、@Repository注解的,但没有在spring容器注册过的bean无效。 **

**context:component-scan base-package **

该配置可以扫描base-package指定包下 包下@Component、@Controller、@Service、@Repository注解并将被注解bean注册到spring容器内,使之生效.

xm约束

<?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.dyz.pojo"/>
</beans>

User实体加@Component注解

import org.springframework.stereotype.Component;
//等价于 <bean id="user" class="com.dyz.pojo.User"/>
@Component
public class User {
    public String name="张宇";
}

Test

@Test
public void UserTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = context.getBean("user", User.class);
    System.out.println(user.name);

}

基本属性注入

@Component
public class User {
    @Value("张宇")
    public String name;
}

衍生注解

@Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!

  • dao[@Respository]
  • service[@Service]
  • controller[@Controller]
  • pojo[@Component]

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

作用域

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

小结:

xml与注解:

  • xml更加万能,适用于任何场合,维护简单方便
  • 注解不是自己类是用不了,维护相对复杂

xml与注解最佳实践

  • xm用来管理bean
  • 注解用来实现属性注入
  • 我们在使用过程中,只需要注意一个问题,必须让注解生效,那就是开启注解的支持.让扫描包下的注解生效

使用Java的方式来配置Spring

package com.dyz.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
    @Value("张宇")
    private String name;

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

    public User() {

    }

    public String getName() {
        return name;
    }

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

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

Config

package com.dyz.config;

import com.dyz.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@ComponentScan(value ="com.dyz.pojo") //相当于扫描包的xml约束
@Configuration
@Import(StudentConfig.class)
public class UserConfig {
    @Bean(value = "user2")
    //@Bean就是容器中的Bean标签 id默认是方法名 也可以自定义value Class是方法的返回值
    public User user(){
        return new User();
    }
    @Bean
    public User user3(){
        return new User();
    }
}

Test

import com.dyz.config.UserConfig;
import com.dyz.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class UserTest {
    @Test
    public void MyTest(){
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        User user3 = context.getBean("user3", User.class);
        System.out.println(user3.getName());
    }
}

代理模式

角色分析

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色之后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

代理模式的好处:

  • 可以使真是角色的操作更加纯粹 不用去关注一些公共的业务
  • 公共业务也就交给代理角色 实现业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点;

一个真实角色就会产生一个代理角色,代码量会反被,开发效率会变低

代码步骤

接口

package com.dyz.demo1;

public interface Rent {
    public void rent();

}

真是角色

package com.dyz.demo1;

public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("出租了该房子!");
    }
}

代理角色

package com.dyz.demo1;

public class Proxy implements Rent {
    private Host host;

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

    public Host getHost() {
        return host;
    }

    public void setHost(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        heTong();
        kanFang();
        host.rent();
    }
    public void heTong(){
        System.out.println("签合同");
    }
    public void kanFang(){
        System.out.println("看房子");
    }
}

客户端访问代理角色

package com.dyz.demo1;

public class Client {
    public static void main(String[] args) {
        //租某个房东的房子
        Host host = new Host();
        //中介帮忙代理
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

加深理解

接口

package com.dyz.demo2;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

真是角色

package com.dyz.demo2;

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("查询了数据");
    }
//修改原有代码在公司中是大忌 因此选择代理
}

代理角色

package com.dyz.demo2;

public class Proxy implements UserService {
    public UserServiceImpl user;

    public void setUser(UserServiceImpl user) {
        this.user = user;
    }

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

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

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

    @Override
    public void select() {
        log("select");
        user.select();
    }

    public void log(String str){
        System.out.println("使用了"+str+"方法");
    }
}

客户

package com.dyz.demo2;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl user = new UserServiceImpl();
        Proxy proxy = new Proxy();
        proxy.setUser(user);
        proxy.add();

    }
}

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理 基于类的动态代理

代理接口

package com.dyz.demo3;

public interface Rent {
    public void rent();
}

真实角色

package com.dyz.demo3;

public class Host  implements Rent {
    @Override
    public void rent() {
        System.out.println("出租了一间房屋!");
    }
}

生成动态代理实例(ProxyInvocationHandler)并调用处理程序返回结果(InvocationHandler)

package com.dyz.demo3;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;
    //获取客户传来的真是角色 ,真是角色实现了Rent接口 因此可以是Rent类型
    //如果将此处的rent换成host 那么需要再创建一个host属性  有点麻烦(个人理解)
    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //通过代理方法的反射对象动态的创建一个代理该方法的代理角色类
    public Object getProxy(){
        return  Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    //处理代理实例的方法(包括真实角色的方法和代理角色的方法)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        heTong();
        kanFang();
        Object result = method.invoke(rent, args);
        return result;
    }
    public void heTong(){
        System.out.println("签了一份合同");
    }
    public void kanFang(){
        System.out.println("看了好多房");
    }
}

使用Spring实现Aop

使用Aop植入需要导入一个依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
    <scope>runtime</scope>
</dependency>

方式一:使用Spring的API接口

接口

package com.dyz.demo1;

public interface UserService {
    public void add();
    public  void delete();
    public  void update();
    public void select();
}

实现类

package com.dyz.demo1;

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("查询了一个用户");
    }

}

切面

前置通知

package com.dyz.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class beforeLog implements MethodBeforeAdvice {
    //method:Spring容器中目标对象的方法
    //target:目标对象 可以通过反射获取该类的名称等等
    //args:参数
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法");
    }
}

后置通知

package com.dyz.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class afterLog implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了,返回值为"+returnValue);
    }
}

Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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.dyz.demo1.UserServiceImpl"/>
    <bean id="beforeLog" class="com.dyz.log.beforeLog"/>
    <bean id="afterLog" class="com.dyz.log.afterLog"/>

<!--    配置aop:需要导入aop约束-->
    <aop:config>
<!--        execution(返回值类型任意 包名.各种方法(任何参数类型) )
            execution(* com.dyz.demo1.UserServiceImpl.*(..)) )
            需要被切入的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.dyz.demo1.UserServiceImpl.*(..) )"/>
<!--        执行环绕增加 切入某个方法-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
    
</beans>

测试

import com.dyz.demo1.UserService;
import com.dyz.demo1.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext");
        //动态代理代理的是接口 此处用到多态的特性
        UserService userService = (UserService) context.getBean("userService");
        userService.select();

    }
}

Aop实现方式二(自定义)

接口

package com.dyz.demo1;

public interface UserService {
    public void add();
    public  void delete();
    public  void update();
    public void select();
}

实现类

package com.dyz.demo1;

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("查询了一个用户");
    }

}

切面

package com.dyz.log;

public class Diy {
    public void before(){
        System.out.println("========方法执行前==========");
    }
    public void after(){
        System.out.println("=========方法执行后=========");
    }
}

xml配置

    <bean id="userService" class="com.dyz.demo1.UserServiceImpl"/>    
<bean id="diy" class="com.dyz.log.Diy"/>
    
    <aop:config>
<!--        ref为引用bean容器中注册的切入程序beanid-->
        <aop:aspect ref="diy">
            <aop:pointcut id="point" expression="execution(* com.dyz.demo1.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    

Aop实现方式三(注解实现)

接口

package com.dyz.demo1;

public interface UserService {
    public void add();
    public  void delete();
    public  void update();
    public void select();
}

实现类

package com.dyz.demo1;

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("查询了一个用户");
    }

}

切面

package com.dyz.log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect//标注该类是一个切面
public class AnnotationPointcut {
    @Before("execution(* com.dyz.demo1.UserServiceImpl.*(..))")//切入点
    public void before(){
        System.out.println("方法执行");
    }
    @After("execution(* com.dyz.demo1.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }
    @Around("execution(* com.dyz.demo1.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("方法执行前环绕");
        try {
            //执行方法
            Object proceed = pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("方法执行后环绕");
    }
}

xml配置

<bean id="userService" class="com.dyz.demo1.UserServiceImpl"/>

    <bean id="inPointcut" class="com.dyz.log.AnnotationPointcut"/>
<!--    开启aop自动代理注解支持 基于接口JDK(proxy-target-class="false) 默认为false 基于类CJlib(proxy-target-class="true)-->
    <aop:aspectj-autoproxy />

整合Mybatis

步骤:

  1. 导入相关jar包

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.19</version>
        </dependency>
    <!--    spring操作数据库的包-->
        <dependency>
            <groupId>springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.singledog</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
        </dependency>
    </dependencies>
    
  2. 编写配置文件

  3. 测试

Mybatis-Spring

  1. 编写数据源配置

  2. SqlSessionFactory

  3. SqlSessionTemplate

    <?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">
    <!--dataSource 使用Spring的数据源替换Mybatis的配置-->
    <!--sqlSessionFactory-->
    
    <!--    dataSource-->
        <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&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>
    
    <!--   SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        相当于  InputStream in= Resources.getResourceAsStream("Mybatis-config.xml");-->
            <property name="dataSource" ref="dataSource" />
            <!--        相当于原来Mybatis中注册mapper-->
            <property name="mapperLocations" value="classpath:com/dyz/mapper/*.xml"/>
            <property name="configLocation" value="classpath:Mybatis-config.xml"/>
        </bean>
    
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--        通过构造器赋值SqlSessionFactory来创建sqlSession -->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
    
    </beans>
    
  4. 需要给接口加实现类

    package com.dyz.mapper;
    
    import com.dyz.pojo.User;
    import org.mybatis.spring.SqlSessionTemplate;
    
    import java.util.List;
    
    public class UserMapperImpl implements UserMapper {
        /*原来是通过SqlSessionFactory创建sqlSession来执行 现在通过
        SqlSessionTemplate来执行
         */
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public List<User> selectUser() {
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                return mapper.selectUser();
        }
    }
    
  5. 将自己写的实现类注入到Spring中

  6. <?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">
        <!--dataSource 使用Spring的数据源替换Mybatis的配置-->
        <!--sqlSessionFactory-->
    <import resource="Spring-dao.xml"/>
        <bean id="userMapper" class="com.dyz.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    
    
    </beans>
    
  7. 测试使用

import com.dyz.mapper.UserMapper;
import com.dyz.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    @Test
    public void UserMapperTest() throws IOException {
       ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> users = userMapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

整合方式二(不需要注入SqlSessionTemplate)

<?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">
<!--dataSource 使用Spring的数据源替换Mybatis的配置-->
<!--sqlSessionFactory-->

<!--    dataSource-->
    <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&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

<!--   SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        相当于  InputStream in= Resources.getResourceAsStream("Mybatis-config.xml");-->
        <property name="dataSource" ref="dataSource" />
        <!--        相当于原来Mybatis中注册mapper-->
        <property name="mapperLocations" value="classpath:com/dyz/mapper/*.xml"/>
        <property name="configLocation" value="classpath:Mybatis-config.xml"/>
    </bean>

<!--    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
<!--&lt;!&ndash;        通过构造器赋值SqlSessionFactory来创建sqlSession &ndash;&gt;-->
<!--        <constructor-arg index="0" ref="sqlSessionFactory"/>-->
<!--    </bean>-->
    
        <bean id="userMapper" class="com.dyz.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>


</beans>

实现类

package com.dyz.mapper;

import com.dyz.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

声明式事务

  1. 回顾事务
    • 把一组业务当成一个业务来做,要么都成功,要么都失败
    • 事务在项目开发中,十分重要,涉及到数据的一致性问题
    • 确保完整性和一致性
  2. 事务ACID原则
    • 原子性
    • 一致性
    • 隔离性
      • 持久性

Spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中进行事务的管理

为什么需要事务:

  • 如果不配置事务,可能存在数据提交不一致的情况
  • 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性

xml约束

注意:先短再长

第一个不变

xmlns:tx=“http://www.springframework.org/schema/tx”

先http://www.springframework.org/schema/tx

再https://www.springframework.org/schema/tx/spring-tx.xsd"

 xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd"


<!--&lt;!&ndash;    配置声明式事务&ndash;&gt;-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--&lt;!&ndash;    结合AOP实现事务植入&ndash;&gt;-->
<!--&lt;!&ndash;    配置事务通知&ndash;&gt;-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--    给哪些方法配置事务-->
        <tx:attributes>
<!--            propagation Spring的默认事务属性 如果当前没有事务就自动创建一个-->
            <tx:method name="add" 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="pointcut" expression="execution(* com.dyz.mapper.*.*(..))"/>
<!--        把事务切入到方法中(切入点中)-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值