Spring学习

spring入门

1.IOC基础

分析实现

dao层:使用不同的接口完成对不同数据库的操作

public interface UserDao {
    void gerUser();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void gerUser() {
        System.out.println("默认获取用户的数据");
    }
}
public class UserDaoMysqlImpl implements UserDao {
    @Override
    public void gerUser() {
        System.out.println("使用mysql存储数据");
    }
}
public class UserDaoOracleImpl implements UserDao {
    @Override
    public void gerUser() {
        System.out.println("使用oracle存储数据");
    }
}

业务层(Service):调用dao层方法

public interface UserService {
    void getUser();
}
public class UserServiceImpl implements UserService {
    private UserDao userDao;
//    传统方法要使用oracle或者mysql数据库都需要更改源代码,这就违反了
//    开闭原则:
//    当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
//    private UserDao userDao = new UserDaoMysqlImpl();
//    private UserDao userDao = new UserDaoOracleImpl();

    //这里使用set来动态实现方法的调用而不需要改变原业务层代码,修改一次代码的成本大
    //传统方法的程序是主动创建对象,控制权在程序员手上
    //使用set注入后,程序不再具有主动性,而是变成了被动接受对象
    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
    }

    @Override
    public void getUser() {
        userDao.gerUser();
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        //用户实际调用的是业务层,dao层不需要接触
        UserService userService = new UserServiceImpl();
        //使用set完成对象的注入,不像传统方式要调用不同的数据库要手动更改业务层的代码
        ((UserServiceImpl)userService).setUserDao(new UserDaoMysqlImpl());
        userService.getUser();

    }
}
  • 这种思想在本质上解决了需要反复更改业务层代码的问题,我们就不需要在业务层去管理对象的创建,系统的耦合性大大降低,可以更加专注于业务的实现。
  • 通俗一点讲就是将创建对象的控制权交给用户而不是程序员了,这就是IOC的原型

2.IOC本质

控制反转IOC(Inversion Of Control)是一种设计思想,DI(依赖注入)只是IOC的一种实现方式,可以认为所谓的控制反转就是获得依赖对象的方式转变了
Spring学习_第1张图片

  • Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

Spring学习_第2张图片

  • 采用xml方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在是实现类中,从而达到零配置的目的。
  • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

3.HelloSpring

3.1 导入Jar包

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.9version>
dependency>

3.2 编写代码

实体类:

public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

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

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

}

编写我们的spring文件 , 这里我们命名为beans.xml(官方命名为applicationContext.xml,这里随便定义都行)


<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="hello" class="com.golven.pojo.Hello">
        <property name="str" value="Spring">property>
    bean>

beans>

测试:

public class MyTest {
    public static void main(String[] args) {
        //实例化容器,凡是通过xml文件配置spring都要这条语句,获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在Spring中管理了,我们要使用直接去里面取出来就行
        Hello hello = (Hello)context.getBean("hello");
        System.out.println(hello.toString());
    }
}

在案例中,hello对象是由spring创建的,hello对象的属性是由spring容器设置的

这个过程就叫控制反转

  • 控制:谁来控制对象的创建,传统应用程序的对象是由应用程序本身控制创建的,使用spring后,对象是由spring创建的
  • 反转:程序本身不创建对象,而变成被动接收对象

依赖注入 : 就是利用set方法来进行注入的,如果将实体类中的set方法删去,则无法注入

4.IOC创建对象方式

4.1使用无参构造创建对象

实体类:

public class User {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
    public User(){
        System.out.println("User的无参构造");
    }
}

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

    <bean id="user" class="com.golven.pojo.User">
        <property name="name" value="golven"></property>
    </bean>

</beans>
javapublic class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //注意到这里getBean时Spring采用无参构造创建对象
        User user = (User)context.getBean("user");
        System.out.println(user.toString());
    }
}

4.2 使用有参构造创建对象

  • 下标赋值
第一种,采用下标对应参数
<bean id="user" class="com.golven.pojo.User">
    <constructor-arg index="0" value="我干">constructor-arg>
bean>
  • 数据类型赋值
第二种,通过数据类型创建
<bean id="user" class="com.golven.pojo.User">
    <constructor-arg type="java.lang.String" value="搞快点">constructor-arg>
bean>
  • 变量名赋值
第三种,通过参数名创建
<bean id="user" class="com.golven.pojo.User">
    <constructor-arg name="name" value="我命由我不由天">constructor-arg>
bean>

总结,在配置文件加载的时候,IOC中的对象就全部都创建了

5.Spring配置

5.1 别名

 
<alias name="user" alias="userNew">alias>

5.2 Bean的配置



<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
    <property name="name" value="Spring"/>
bean>

5.3 Import

这个import一般用于团队开发使用,它可以将多个配置文件导入合并为一个配置文件

假设现在团队中有多个人开发,这三个人有不同的类,不同的类需要注册在不同的bean中,我们可以利用import将所有的beans.xml合并为一个总的

applicationContext.xml

<import resource="beans.xml">import>

6. 依赖注入(DI)

6.1 构造器注入

前面说过了

6.2 Set方式注入

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

实体类:

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    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> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    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) +
                ", hobbies=" + hobbies +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
  • 常量注入
<bean id="student" class="com.golven.pojo.Student">
    
    <property name="name" value="golven">property>
bean>
  • Bean注入
<bean id="add" class="com.golven.pojo.Address">
    <property name="address" value="荆州">property>
bean>

<bean id="student" class="com.golven.pojo.Student">
    <property name="name" value="golven">property>
    <property name="address" ref="add">property>
bean>
  • 数组注入
<bean id="student" class="com.golven.pojo.Student">
    <property name="books">
        <array>
            <value>西游记value>
            <value>红楼梦value>
            <value>水浒传value>
        array>
    property>
bean>
  • List注入
<bean id="student" class="com.golven.pojo.Student">
    <property name="hobbies">
            <list>
                <value>唱歌value>
                <value>跳舞value>
            list>
    property>
    bean>
  • Map注入
<bean id="student" class="com.golven.pojo.Student">
    <property name="card">
        <map>
            <entry key="中国移动" value="123456789">entry>
            <entry key="中国联通" value="155555555">entry>
        map>
    property>
bean>
  • Set注入
<bean id="student" class="com.golven.pojo.Student">
    <property name="games">
        <set>
            <value>LOLvalue>
            <value>三国杀value>
        set>
    property>
    
bean>
  • null注入
<property name="wife"><null/>property>
  • Properties注入
<property name="info">
    <props>
        <prop key="学号">1705110119prop>
        <prop key="性别">prop>
    props>
property>

测试类:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student)context.getBean("student");
        System.out.println(student.getName());
        System.out.println(student.getAddress().getAddress());
        System.out.println(Arrays.toString(student.getBooks()));
        System.out.println(student.getHobbies().toString());
        System.out.println(student.getCard());
        System.out.println(student.getGames());
        System.out.println(student.getWife());
        System.out.println(student.getInfo());
    }
}

6.3 拓展方式注入

实体类:

public class User {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
  • p命名空间注入:需要在头文件中加入约束条件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"

<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
  • c命名空间注入:需要在头文件中加入约束条件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"

<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>

发现问题:爆红了,刚才我们没有写有参构造!

解决:把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!

测试代码:

@Test
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    //有User.class就不用强转
    User user = context.getBean("user",User.class);
    System.out.println(user.toString());
}

6.4 Bean的作用域

Spring学习_第3张图片

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

Spring学习_第4张图片

<bean id="user" class="com.golven.pojo.User" p:name="golven" p:age="21" scope="singleton">bean>
  • 原型模式:每次容器中get的时候都会产生一个新的对象
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/> 
  • 其余的request、session、application都是在web开发中才用到

7. Bean的自动装配

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

在spring中有三种装配方式:

  • 在xml中显示的配置
  • 在java中显示的配置
  • 隐式的自动装配bean【important!!】

7.1 搭建测试环境

实体类:

public class Person {
    private String name;
    private Cat cat;
    private Dog dog;

    public Person(String name, Cat cat, Dog dog) {
        this.name = name;
        this.cat = cat;
        this.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;
    }
}
public class Cat {
    public void shout(){
        System.out.println("喵喵");
    }
}
public class Dog {
    public void shout(){
        System.out.println("汪汪");
    }
}

spring配置文件:


<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="dog" class="com.golven.pojo.Dog">bean>
    <bean id="cat" class="com.golven.pojo.Cat">bean>
    
    <bean id="person" class="com.golven.pojo.Person">
        <property name="name" value="小哪吒">property>
        <property name="cat" ref="cat">property>
        <property name="dog" ref="dog">property>
    bean>

beans>

测试:

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

7.2 ByName

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

<bean id="person" class="com.golven.pojo.Person" autowire="byName">
    <property name="name" value="小哪吒">property>
bean>

7.3 ByType

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


<bean id="person" class="com.golven.pojo.Person" autowire="byType">
    <property name="name" value="小哪吒">property>
bean>

小结:

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

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

7.4 使用注解实现的自动装配

要使用注解须知:

  • 导入约束:context约束条件
  • 配置支持的注解:开启注解支持

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

beans>

@Autowired注解

@Autowired是按类型(byType)自动转配的,不支持byName匹配。

直接在类中的属性上使用,也可以在set方法上使用

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

tips:

@Nullable 字段标记了这个注解,说明这个字段可以为空
@Autowired(required=false) 说明: false,对象可以为nulltrue,对象必须存对象,不能为null

测试代码:

//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;

@Qualifier注解

  • @Autowired是根据类型自动装配的,加上@Qualifier]则可以根据byName的方式自动装配
  • @Qualifier不能单独使用

测试:

​ 配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

​ 没有加Qualifier测试,直接报错

​ 在属性上添加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;

@Resource注解

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

实体类:

public class User {
    //如果允许对象为null,设置required = false,默认为true
    @Resource(name = "cat2")
    private Cat cat;
    @Resource
    private Dog dog;
    private String str;
}

beans.xml

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>

测试结果成功

配置文件2:beans.xml , 删掉cat2

<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>

实体类只保留注解

@Resource
private Cat cat;
@Resource
private Dog dog;

结果:OK

结论:先进行byName查找,失败;再进行byType查找,成功。

@Autowired和@Resource注解的区别

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

8. 使用注解开发

8.1 说明

在spring4之后,想要使用注解形式,必须要引入aop的包

Spring学习_第5张图片

在配置文件中还应该引入context约束


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
beans>

8.2 Bean的实现

之前我们都是使用bean标签在xml文件中注入,但是在实际开发中,我们一般都会使用注解

  • 配置扫描哪些包下面的注解

<context:component-scan base-package="com.golven.pojo"/>
  • 在指定的包中编写类
@Component("user")
//相当于配置文件中 
public class User {
    public String name="golven";
}
  • 测试
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = context.getBean("user", User.class);
    System.out.println(user.name);
}

8.3 属性注入

使用@Value注入属性,可以不用提供set方法,直接在直接名上添加@value(“值”)

@Component("user")
//相当于配置文件中 
public class User {
    @Value("golven")
    //相当于配置文件中的
    public String name;
}

8.4 衍生的注解

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

  • dao【@Repository】
  • service【@Service】
  • controller【@Controller】

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

8.5 作用域

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {
    @Value("golven")
    public String name;
}

小结:xml和注解

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

xml与注解的最佳实践:

  • xml只用来管理bean
  • 注解用来完成属性的注入
  • 我们在是使用的过程中,只需要注意一个问题就是使注解生效

8.6 基于Java类配置IOC

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

实体类:

//将这个类注入到IOC容器中
@Component
public class User {
    @Value("GOLVEN")
    private String name;

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

    public String getName() {
        return name;
    }

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

新建一个config包,编写一个MyConfig配置类

@Configuration
//这里的@Configuration代表一个配置类,就相当于之前的xml配置文件
//这个也会在Spring容器中托管,注册到容器中,因为他本来就是一个Component
@ComponentScan("com.golven.pojo")
public class MyConfig {

    //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
    //相当于之前xml文件中的bean标签
    @Bean
    public User getUser(){
        return new User();
    }
}

测试:

@Test
public void test(){
    //使用AnnotationConfigApplicationContext获取到IOC容器
    //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
    ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
    User getUser = context.getBean("getUser", User.class);
    System.out.println(getUser.toString());
}
  • 可以使用@Import注解在一个配置类中导入另一个配置类
@Import(MyConfig2.class)

9.代理模式

为什么要学习代理模式,因为AOP的底层机制就是代理模式

代理模式:

  • 动态代理
  • 静态代理

Spring学习_第6张图片

9.1 静态代理

角色分析:

  • 抽象角色:一般使用接口或抽象类来实现
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后一般会做一些附属的操作
  • 客户:使用代理角色来进行一些操作

代码实现:

Rent.java 抽象角色

public interface Rent {
    public void rent();
}

Host . java 即真实角色

public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房屋出租");
    }
}

Proxy.java 代理角色

public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {

        this.host = host;
    }
    //租房
    @Override
    public void rent() {
        seeHouse();
        host.rent();
        fare();
    }
    //看房
    public void seeHouse(){
        System.out.println("看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

Client.java 客户

//客户类,一般客户都会去找代理!
public class Client {
    public static void main(String[] args) {
        //房东要租房
        Host host = new Host();
        //中介帮房东租房
        Proxy proxy = new Proxy(host);
        //客户找中介租房
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共也就交给了代理角色,实现了角色分工
  • 公共业务发生扩展的时候,方便集中管理
  • 在不改变原有代码的基础上做一些修改会更有优势,符合开闭原则,改动部分采用代理模式试错成本更小

缺点:

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

9.2 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类市自动生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口----JDK动态代理【我们这里使用这个】
    • 基于类:cglib
    • java字节码实现:javassist

需要了解两个类:

  • Proxy,提供创建动态代理类的静态方法
  • InvocationHandler调用动态代理程序的接口,并返回结果

代码实现:

Rent.java 抽象角色

public interface Rent {
    public void rent();
}

Host. java 即真实角色

public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房屋出租");
    }
}

ProxyInvocationHandler.java 动态生成代理角色

//等会我们会用这个动态代理类的处理程序自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回代理结果
    // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是使用反射机制实现的
        Object result = method.invoke(rent, args);
        return result;
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //真实的代理角色
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        proxyInvocationHandler.setRent(host);
        //这里的proxy就是动态生成的,我们没有写
        Rent proxy = (Rent) proxyInvocationHandler.getProxy();
        proxy.rent();
    }
}

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、

深化理解:

将上面的Rent变成Object就是通用的模板了

//等会我们会用这个动态代理类的处理程序自动生成代理类
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);
    }

    //处理代理实例,并返回代理结果
    // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
    @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("执行了"+msg+"方法");
    }
}
public class Client {
    @Test
    public void Test01(){
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        //设置要代理的对象
        proxyInvocationHandler.setTarget(userService);
        UserService proxy = (UserService)proxyInvocationHandler.getProxy();
        proxy.add();
    }

}

10. AOP

10.1 什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个特点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Spring学习_第7张图片

10.2 Spring在AOP中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ….
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

Spring学习_第8张图片

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

Spring学习_第9张图片

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

10.3 使用Spring实现AOP

【important!】导入依赖

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>

10.3.1 方式一:使用Spring的API接口

public interface UserService {
    public void add();
    public void delete();
    public void query();
    public void update();
}
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("查询用户");
    }
}

使用spring原生的API配置环绕增强

public class Log implements MethodBeforeAdvice{
    //method:要执行目标对象的方法
    //objects:被调用的方法的参数
    //Object:目标对象
    @Override
    public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"方法执行了");
    }
}
public class AfterLog implements AfterReturningAdvice {
    /**
     *
     * @param returnValue  返回值
     * @param method 被调用的方法
     * @param args 被调用的方法的对象的参数
     * @param target 被调用的目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法,返回值:"+returnValue);
    }
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <bean id="UserService" class="com.golven.service.UserServiceImpl">bean>
    <bean id="Log" class="com.golven.Log.Log">bean>
    <bean id="AfterLog" class="com.golven.Log.AfterLog">bean>

    
    
    <aop:config>
        
        
        
        <aop:pointcut id="pointcut" expression="execution(* com.golven.service.UserServiceImpl.*(..))">aop:pointcut>

        
        <aop:advisor advice-ref="Log" pointcut-ref="pointcut">aop:advisor>
        <aop:advisor advice-ref="AfterLog" pointcut-ref="pointcut">aop:advisor>

    aop:config>
beans>

测试:注意这里联想到代理模式

public class MyTest {
    @Test
    public void text01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
        UserService userService = context.getBean("UserService", UserService.class);
        userService.add();
    }
}

10.3.2 方式二:自定义类来实现

保持UserService,UserServiceImpl类不变

自己定义一个切入类

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

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <bean id="UserService" class="com.golven.service.UserServiceImpl">bean>
    <bean id="Log" class="com.golven.Log.Log">bean>
    <bean id="AfterLog" class="com.golven.Log.AfterLog">bean>
    
    <bean id="diy" class="com.golven.Diy.DiyLog"/>
    <aop:config>
        
        <aop:aspect ref="diy">
            <aop:pointcut id="pointcut" expression="execution(* com.golven.service.UserServiceImpl.*(..))">aop:pointcut>
            <aop:before method="before" pointcut-ref="pointcut">aop:before>
            <aop:after method="after" pointcut-ref="pointcut">aop:after>
        aop:aspect>
    aop:config>


beans>

测试还是原代码

【主要是在xml文件中定义切面】

10.3.4 方式三:使用注解方式实现AOP

  • 编写注解实现的增强类
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.golven.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=======在方法执行前========");
    }
    @After("execution(* com.golven.service.UserServiceImpl.*(..))")
    public void After(){
        System.out.println("==========在方法执行后===========");
    }
    @Around("execution(* com.golven.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕前:");
        //执行方法
        Object proceed = pj.proceed();

        System.out.println("环绕后:");
    }
}
  • 在spring配置文件中注册bean,并增加支持注解的配置

<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

11. 整合Mybatis

  • 导入依赖
<dependencies>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.2version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.47version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.1.10.RELEASEversion>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-jdbcartifactId>
        <version>5.1.10.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.9.4version>
    dependency>
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatis-springartifactId>
        <version>2.0.2version>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.16.10version>
    dependency>
dependencies>
  • 配置Maven静态资源过滤问题!
<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>
    resources>
build>
  • 新建实体类User
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
  • 编写mybatis配置文件mybatis-config.xml

DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.golven.pojo">package>
    typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver">property>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?seSSL=true&useUnicode=true&characterEncoding=utf8">property>
                <property name="username" value="root">property>
                <property name="password" value="123456">property>
            dataSource>
        environment>
    environments>
    <mappers>
        <package name="com.golven.mapper">package>
    mappers>
configuration>
  • 编写dao层接口
public interface UserMapper {
    public List<User> selectUser();
}
  • 接口对应的maper映射文件

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.golven.mapper.UserMapper">
    <select id="selectUser" resultType="User">
      select * from user
    select>
mapper>
  • 测试:
@Test
public void test01() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = build.openSession();

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

11.1 方式一:

  • 引入spring配置文件

<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">
beans>
  • 配置数据源替换mybaits的数据源


<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver">property>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?seSSL=true&useUnicode=true&characterEncoding=utf8">property>
    <property name="username" value="root">property>
    <property name="password" value="123456">property>
bean>
  • 配置SqlSessionFactory,关联MyBatis

<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource">property>
    
    <property name="configLocation" value="classpath:mybatis-config.xml">property>
    <property name="mapperLocations" value="classpath:com/golven/mapper/*.xml">property>
bean>
  • 注册sqlSessionTemplate,关联sqlSessionFactory;

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    
    <constructor-arg index="0" ref="SqlSessionFactory">constructor-arg>
bean>
  • 增加UserMapper接口的实现类
public class UserMapperImpl implements UserMapper {

    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
  • 注入bean

<bean id="userMapperImpl" class="com.golven.mapper.UserMapperImpl">
    <property name="sqlSessionTemplate" ref="sqlSession">property>
bean>
  • 测试
@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
    UserMapperImpl userMapperImpl = context.getBean("userMapperImpl", UserMapperImpl.class);
    System.out.println(userMapperImpl.selectUser());
}

11.2 方式二:继承SqlSessionDaoSupport

  • 修改UserMapperImpl实现类
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
  • 注入bean
<bean id="userMapperImpl2" class="com.golven.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="SqlSessionFactory">property>
bean>
  • 测试
@Test
public void test03(){
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
    UserMapperImpl2 userMapperImpl = context.getBean("userMapperImpl2", UserMapperImpl2.class);
    System.out.println(userMapperImpl.selectUser());
}

第二种方式就不需要在UserMapperImpl中定义SqlSessionTemplate属性,直接getSession就能拿到SqlSession

12. 声明式事务

12.1 事务回顾

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

  1. 原子性(atomicity)
    • 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
  2. 一致性(consistency)
    • 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
  3. 隔离性(isolation)
    • 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
  4. 持久性(durability)
    • 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

12.2 Spring中的事务管理

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

事务的配置:


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource">property>
bean>



<tx:advice id="txAdvice" transaction-manager="transactionManager">
    
    <tx:attributes>
        <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="txPointCut" expression="execution(* com.golven.mapper.*.*(..))">aop:pointcut>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut">aop:advisor>
aop:config>

为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;
  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

你可能感兴趣的:(Spring,spring,java)