其他分享
首页 > 其他分享> > Spring5从基础入门到深入理解IOC、DI与AOP原理

Spring5从基础入门到深入理解IOC、DI与AOP原理

作者:互联网

Spring框架在Java开发中占有极其重要的地位,但是到底什么是Spring,Spring怎么使用以及为什么要使用Spring,接下来让我们详细的了解一下!!!

注:本篇文章大部分内容参照于B站狂神说老师的Spring5视频编写,仅供大家参考学习,重点内容已经做了标记,视频原地址为【狂神说Java】Spring5最新完整教程IDEA版通俗易懂,大家记得一键三连啊.。(๑・∀・๑).

1.Spring

1.1简介

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

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

1.2优点

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

1.3组成

1.4扩展

因为现在大多数公司都在使用SpringBooti进行快速开发,学习SpringBoot的前提,需要完全掌握Spring.及
SpringMVC!承上启下的作用!

2.IOC理论推导

2.1实现程序进行理解

  1. UserDao接口

    public interface UserDao {
        void getUser();
    }
    
  2. UserDaolmpl实现类

    public class UserDaoImpl implements UserDao {
        @Override
        public void getUser() {
            System.out.println("默认获取用户的数据");
        }
    }
    
  3. UserService业务接口

    public interface UserService {
        void getUser();
    }
    
  4. UserServicelmpl业务实现类

public class UserServiceImpl implements UserService{
    private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserDaoMysqlImpl();
    
    @Override
    public void getUser() {
        userDao.getUser();

    }
}

5.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">

    <!--使用Spring创建对象,在Spring这些都称为bean
          类型 变量名  = new 类型();
          Hello hello = new Hello();
          bean = 变量名   new Hello();
          id=变量名
          class=new的对象
          property相当于给对象中的属性设置一个值
      -->
    <bean id="mysqlImpl" class="com.qjd.dao.UserDaoMysqlImpl"/>
    <bean id="oracleImpl" class="com.qjd.dao.UserDaoOracleImpl"/>
    <bean id="UserServiceImpl" class="com.qjd.service.UserServiceImpl">
        <!--ref:引用Spring容器中创建好的对象
            value:就是具体的值,基本数据类型
         -->
        <property name="userDao" ref="mysqlImpl" />
    </bean>

</beans>

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

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

public class UserServiceImpl implements UserService{
     private UserDao userDao;
//利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void getUser() {
        userDao.getUser();

    }
}

这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !

2.2IOC本质

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

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。

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

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

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

原文链接:https://blog.csdn.net/qq_33369905/article/details/106647330

3.HelloSpring

1、编写一个Hello实体类

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 + '\'' +
                '}';
    }
}

2、编写我们的spring文件 , 这里我们命名为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">

<!--使用Spring创建对象,在Spring这些都称为bean
      类型 变量名  = new 类型();
      Hello hello = new Hello();
      bean = 变量名   new Hello();
      id=变量名
      class=new的对象
      property相当于给对象中的属性设置一个值
  -->
    <bean id="hello" class="com.qjd.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>

</beans>

3、测试

public class MyTest {
    public static void main(String[] args) {
        //使用xml加载就必须使用这句话
        //获取Spring的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象现在都在Spring中管理了,我们要使用,直接去里面取出来就可以了
        Hello hello =(Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

思考

这个过程就叫控制反转 :

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

现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所以总结IOC:对象由Spring创建,管理,装配

4.IOC创建对象的方式

4.1、使用无参构造方法来创建对象

4.2、使用有参构造方法来创建对象

总结:在配置文件加载的时候,容器中的管理对象就已经初始化了

5.Spring配置(重点)

5.1别名

<alias name="user" alias="okk"/>

5.2Bean的配置

<!--
id:bean的唯一标识符,也就是我们学的对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名,name可以同时取多个别名
-->
<bean id="UserT" class="com.qjd.pojo.UserT" name="user2 u2,u3;u4" >
    <property name="name" value="qjd"/>
</bean>

5.3import

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

将不同人编写bean合并在一个applicationContext.xml中

<import resource="beans.xml"/>
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>

6.依赖注入(重点)

6.1构造器注入

参照4

6.2Set方式注入【重点】

【搭建环境】

  1. 复杂类型-----Address

    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. 真实测试对象----Student

    public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbys;
        private Map<String,String> card;
        private Set<String> game;
        private String wife;
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", address=" + address.toString() +
                    ", books=" + Arrays.toString(books) +
                    ", hobbys=" + hobbys +
                    ", card=" + card +
                    ", game=" + game +
                    ", wife='" + wife + '\'' +
                    ", info=" + 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> getHobbys() {
            return hobbys;
        }
    
        public void setHobbys(List<String> hobbys) {
            this.hobbys = hobbys;
        }
    
        public Map<String, String> getCard() {
            return card;
        }
    
        public void setCard(Map<String, String> card) {
            this.card = card;
        }
    
        public Set<String> getGame() {
            return game;
        }
    
        public void setGame(Set<String> game) {
            this.game = game;
        }
    
        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;
        }
    
        private Properties info;
    
    }
    
  3. beans.xml

    同5

  4. 测试类

    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.toString());
    /*
    * Student{
    * name='张三',
    * address=Address{address='大连'},
    * books=[三体, 西行纪, 魁拔],
    * hobbys=[唱, 跳, rap, 打篮球],
    * card={身份证=1234567890, 银行卡=0987654321},
    * game=[COD, CSGO, PUBG], wife='null',
    * info={学号=20030099, 性别=男, password=123456, url=........, driver=..., 姓名=李四, username=qjd}}
    * */
       }
    }
    
    
  5. 完善注入信息

    <?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.qjd.pojo.Address">
            <property name="address" value="大连"/>
        </bean>
    
        <bean id="student" class="com.qjd.pojo.Student" >
            <!-- 第一种 普通值注入,value-->
            <property name="name" value="张三"/>
            <!-- 第二种 Bean注入,ref-->
            <property name="address" ref="address"/>
            <!-- 数组注入-->
            <property name="books">
                <array>
                    <value>三体</value>
                    <value>西行纪</value>
                    <value>魁拔</value>
                </array>
            </property>
            <!-- list注入-->
            <property name="hobbys">
                <list>
                    <value>唱</value>
                    <value>跳</value>
                    <value>rap</value>
                    <value>打篮球</value>
                </list>
            </property>
            <!-- Map注入-->
            <property name="card">
                <map>
                    <entry key="身份证" value="1234567890"/>
                    <entry key="银行卡" value="0987654321"/>
                </map>
            </property>
            <!-- Map注入-->
            <property name="game">
                <set>
                    <value>COD</value>
                    <value>CSGO</value>
                    <value>PUBG</value>
                </set>
            </property>
            <!-- 空值注入-->
            <property name="wife" >
                <null/>
            </property>
            <!-- Properties注入
                 key=value
             -->
            <property name="info">
                <props>
                    <prop key="学号">20030099</prop>
                    <prop key="性别">男</prop>
                    <prop key="姓名">李四</prop>
                    <prop key="driver">...</prop>
                    <prop key="url">........</prop>
                    <prop key="username">qjd</prop>
                    <prop key="password">123456</prop>
                </props>
            </property>
    
        </bean>
    
    </beans>
    

6.3拓展方式

我们可以使用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"
       <!--导入p命名-->
       xmlns:p="http://www.springframework.org/schema/p"
       <!--导入c命名   在实体类中加入有参和无参构造-->
       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命名空间注入,可以直接注入属性的值:properties-->
    <bean id="user" class="com.qjd.pojo.User" p:name="王五" p:age="19"/>


    <!--    c命名空间注入,可以通过构造器注入属性的值:construct-args-->
    <bean id="user2" class="com.qjd.pojo.User" c:age="18" c:name="王六"/>

    
</beans>

测试:

    @Test
    public void test2(){
     ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
//        User user =(User) context.getBean("user");
        User user1 = context.getBean("user2", User.class);
        System.out.println(user1);

    }

注意:p命名和c命名空间不能直接使用,需要导入xml约束

6.4bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象

  1. 单例模式(Spring默认机制,以下是显式表示)

    <bean id="user2" class="com.qjd.pojo.User" c:age="18" c:name="王六" scope="singleton"/>
    
  2. 原型模式:每次从容器中get的时候都会产生一个新对象

    <bean id="user2" class="com.qjd.pojo.User" c:age="18" c:name="王六" scope="prototype"/>
    
  3. request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境

【搭建环境步骤】

  1. 在pojo下创建实体类(包括复杂类型和真实测试对象)
  2. 在resources下编写beans.xml
  3. 测试

7.Bean的自动装配(重点)

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;(参照5)
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配【重要】

7.1测试

1.搭建环境: 一个人有两个宠物

7.2ByName自动装配

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

7.3ByType自动装配

    <bean id="cat" class="com.qjd.pojo.Cat"/>
    <bean id="dog1" class="com.qjd.pojo.Dog"/>

    <!--
    byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的 bean id
    byType:会自动在容器上下文中查找,和自己对象属性类型相同 bean   id也可以省略
    -->
    <bean id="people" class="com.qjd.pojo.People" autowire="byType">
        <property name="name" value="老八"/>

    </bean>

小结:

7.4使用注解自动装配

jdk1.5开始支持注解,spring2.5开始全面支持注解

要使用注解须知:

7.4.1@Autowired

直接在属性上使用即可,也可以在set方式上使用

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

测试:

1、将User类中的set方法去掉,使用@Autowired注解

public class User {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String str;
 
    public Cat getCat() {
        return cat;
    }
    public Dog getDog() {
        return dog;
    }
    public String getStr() {
        return str;
    }
}

2、此时配置文件内容

<context:annotation-config/>
 
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>

3、测试结果成功

科普:

@Nullable:字段标记了这个注解,说明这个字段可以为null

@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。

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

@Qualifier

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以
使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!

测试

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

<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"/>

2、没有加Qualifier测试,直接报错

3、在属性上添加Qualifier注解

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

测试,成功输出!

7.4.2@Resource

测试:

1、实体类

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

2、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"/>
<bean id="user" class="com.kuang.pojo.User"/>

3、测试成功

4、修改配置文件

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

5、实体类只保留注解

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

测试结果:成功

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

小结
@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在属性字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

8.使用注解开发

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

在配置文件当中,还得要引入一个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:annotation-config/>

</beans>
  1. bean

    import org.springframework.stereotype.Component;
    //等价于<bean id="user" class="com.qjd.pojo.User"/>
    //@Component 组件
    @Component
    public class User {
        public String name="张三";
    }
    
  2. 属性如何注入

    public class User {
        //等价于 <bean id="user" class="com.qjd.pojo.User">
        //        <property name="name" value="陆毅"/>
        //    </bean>
        //也可以放在set方法上
        @Value("陆毅")
        public String name;
    }
    
  3. 衍生的注解

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

    ​ 1.dao【@Repository】

    ​ 2.service【@Service】

    ​ 3.controller【@Controller】

    这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean,只是在不同的包下使用

  4. 自动装配

    1.@Autowired:自动装配通过类型,名字
          如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")去配置@Autowired的使用
    
    2.@Nullable:字段标记了这个注解,说明这个字段可以为null
    
    3.@Resource:自动装配通过名字,类型
    
  5. 作用域

    @Scope("")
    
  6. 小结

    XML与注解比较

    • XML可以适用任何场景 ,结构清晰,维护方便,更加万能
    • 注解不是自己提供的类使用不了,开发简单方便,维护相对复杂

xml与注解整合开发 :推荐最佳实践

9.使用Java的方式配置Spring

我们现在要完全不使用Spring的xml配置了,全权交给Java来做

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

测试:spring-07-appconfig

1.pojo(实体类)

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这里这个注解的意思是说明这个类被Spring接管,注册到了容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }
    //给属性注入值
    @Value("okkk")
    public void setName(String name) {
        this.name = name;
    }

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

2.config(相当于配置文件)

//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component,
// @Configuration代表这是一个配置类,就类似于beans.xml
@Configuration
@ComponentScan("com.qjd")
@Import(QjdConfig2.class)
public class QjdConfig {
//注册一个bean就相当于之前写的一个bean标签,
// 这个方法的名字相当于id,返回值相当于class
    @Bean
    public User getUser(){
        return new User();
        //就是返回要注入到bean的对象
    }

}

3.MyTest

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类的方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(QjdConfig.class);
        //这里取得是方法名
        User user = context.getBean("getUser", User.class);
        System.out.println(user.getName());
    }
}

10.代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理! 【SpringAOP和SpringMVC必问】

学习代理模式会大量用到反射的知识,如果反射的知识遗忘可以参考以下文章:
Java---注解与反射

代理模式:

10.1静态代理

静态代理角色分析

测试:spring-08-proxy

  1. 接口

    //租房
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host  implements Rent{
        public void rent(){
            System.out.println("房东要出租房子");
        }
    
    }
    
  3. 代理角色

    public class Proxy implements Rent{
        private Host host;
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public Proxy() {
        }
    
        @Override
        public void rent() {
            seeHouse();
            host.rent();
            hetong();
            fare();
    
        }
        //看房
        public void seeHouse(){
            System.out.println("中介领你看房");
        }
        //收中介费
        public void fare(){
            System.out.println("收中介费");
        }
    
        //签合同
        public void hetong(){
            System.out.println("签租赁合同");
        }
    }
    
  4. 客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要出租房子
            Host host = new Host();
            //去找代理,代理帮房东,一般会有附属操作
            Proxy proxy = new Proxy(host);
    
            proxy.rent();
        }
    }
    

静态代理的好处:

可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .

公共的业务由代理来完成 . 实现了业务的分工 ,

公共业务发生扩展时变得更加集中和方便 .

缺点 :

类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

10.2动态代理

动态代理的角色和静态代理的一样 .

动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

现在用的比较多的是 javasist 来生成动态代理 .

我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看

【InvocationHandler:调用处理程序】

代码实现:

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

深化理解:

​ 我们来使用动态代理实现代理我们后面写的UserService!

​ 我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为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 );
    }
    //处理代理实例,并返回结果
    @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 {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理的对象
        pih.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        proxy.update();

    }
}

动态代理的好处

11.AOP

11.1什么是AOP

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

11.2AOP在Spring的作用

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

以下名词需要了解下:

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

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

11.3使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】

代码实现:

首先编写我们的业务接口和实现类

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


    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

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

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

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

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log  implements MethodBeforeAdvice {
    //method:要执行目标的方法
    //Object:参数
    //target:目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");

    }
}
public class AfterLog implements AfterReturningAdvice {

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

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

<?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.qjd.service.UserServiceImpl"/>
    <bean id="log" class="com.qjd.log.Log"/>
    <bean id="after" class="com.qjd.log.AfterLog"/>
<!-- 方式一:使用原生的Spring的API接口   -->
<!-- 配置aop:需要导入aop约束-->
    <aop:config>
<!-- 切入点:我们需要在哪个地方执行Spring的方法
           expression:表达式,execution(要执行的位置)-->
        <aop:pointcut id="pointcut" expression="execution(* com.qjd.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增强,也就是在前后实现输出日志的方法,这里是先实现log再实现afterlog-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/><!--这句话的意思是我们把log这个类切入到UserServiceImpl里的方法上面-->
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>

    </aop:config>
</beans>

测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //动态代理的是接口:注意这里只能使用强转
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

结果:

方式二:使用自定义类来实现AOP【主要是切面定义】

在新包下自定义一个类

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


    }
}

beans.xml

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

        </aop:aspect>
    </aop:config>

MyTest不变测试结果如下

方式三:使用注解实现AOP

自定义一个类

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


    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.qjd.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("环绕前");
        Signature signature = jp.getSignature();//获得签名
        System.out.println("signature"+signature);
        //执行方法
        Object proceed = jp.proceed();
        System.out.println("环绕后");

    }

}

beans.xml

<!--    方式三:使用注解-->

    <bean id="annotationPointCut" class="com.qjd.diy.AnnotationPointCut"/>
    <!--开启注解支持    JDK(默认;proxy-target-class="false")     cglib( proxy-target-class="true")-->
    <aop:aspectj-autoproxy/>

结果;

12.整合MyBatis

步骤:

  1. 导入相关jar包

    junit

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

mybatis

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>

mysql-connector-java

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

spring相关

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.10.RELEASE</version>
</dependency>

aspectJ AOP 织入器

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

mybatis-spring整合包 【重点】

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.2</version>
</dependency>

配置Maven静态资源过滤问题!

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

2.编写配置文件

3.测试

12.1回忆MyBatis

1.编写实体类

2.编写核心配置文件

3.编写接口

4.编写Mapper.xml

5.测试

12.2Mybatis-spring

核心;

<!--sqlSessionRFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
<!-- 绑定mybatis配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:com/qjd/mapper/*.xml"/>
</bean>

 <!--SqlSessionTemplate就是我们在mybatis中使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
 <!--只能使用构造器注入SQLSession,因为它没有set方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!---->
<bean id="userMapper" class="com.qjd.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTempiate

4.需要给接口加实现类

5.将自己写的实现类注入到Spring中,测试使用

大体步骤如下:

13.声明式事务

13.1回顾事务

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

事务四个属性ACID

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

13.2具体步骤

1.新建项目在pom.xml导入依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>
        <!--  mybatis      -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

    </dependencies>

    <!--    Maven配置pom.xml 防止资源导出失败-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

2.建pojo包并建立实体类User

@Data     //使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法
public class User {
    private int id;
    private String name;
    private String pwd;
}

3.mapper包下建立UserMapper接口

public interface UserMapper {
    public List<User> selectUser();
}

4.导入mybatis配置文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!--别名-->
    <typeAliases>
        <package name="com.qjd.pojo"/>
    </typeAliases>
<!--设置-->
<!--  <settings>-->
<!--      <setting name="" value=""/>-->
<!--  </settings>-->

<!--    最好在mybatis的核心配置文件中留两项-->

</configuration>

5.整合mybatis------spring-mapper.xml(dao)

<?xml version="1.0" encoding="UTF8"?>
<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">

    <!--DataSource数据源:使用Spring的数据源替换Mybatis的配置
        我们这里使用Spring提供的JDBC:rg.springframework.jdbc.datasource.DriverManagerDataSource
     -->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>

    </bean>

    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    <!-- 绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/qjd/mapper/*.xml"/>
    </bean>

     <!--SqlSessionTemplate就是我们在mybatis中使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
     <!--只能使用构造器注入SQLSession,因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <!---->
    <bean id="userMapper" class="com.qjd.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

6.把UserMapper.xml放在与接口同包

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--核心配置文件-->
<mapper namespace="com.qjd.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select *from mybatis.user
    </select>
</mapper>

7.把实现类UserMapperImpl放在mapper下于接口同包(这里使用第一种实现方法,就是不继承的那一种)

public class UserMapperImpl implements UserMapper{

    //在原来我们的所有操作都使用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();
    }
}

扩展:以下是另一种方法

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

8.整合配置文件application.xml

<?xml version="1.0" encoding="UTF8"?>
<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="spring-mapper.xml"/>
    <!---->
    <bean id="userMapper" class="com.qjd.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
    
</beans>

9.MyTest测试

public class MyTest {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

}

10.测试结果-----成功

13.3Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

将事务管理代码嵌到业务方法中来控制事务的提交和回滚

缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

一般情况下比编程式事务好用。

将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。

将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

为什么需要配置事务?

到这里关于Spring5的内容就结束了,如果有遗漏和错误欢迎大家提出,我会第一时间改正!!!
最后,我在这里附上Spring中常用的注解,使用注解开发在今后会很常见,所以大家一定要记住常用的注解

常用注解说明

1.@Autowired:自动装配通过类型,名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")去配置@Autowired的使用

2.@Nullable:字段标记了这个注解,说明这个字段可以为null

3.@Resource:自动装配通过名字,类型

4.@Component:组件,这里这个注解的意思是说明这个类被Spring接管,注册到了容器中,类似
@ComponentScan:扫描
@Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层

  ​     1.dao【@Repository】
  
  ​     2.service【@Service】
  
  ​     3.controller【@Controller】
  
  这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean,只是在不同的包下使用

5.@Value(""):给属性注入值,通常写在属性或者set方法上

6.@Configuration代表这是一个配置类,就类似于beans.xml

7.@Import(QjdConfig2.class):引入另一个类

8.@Bean:下面的方法名字相当于id,返回值相当于class

9.@Aspect: 标注这个类是一个切面

标签:String,DI,Spring,void,代理,class,AOP,IOC,public
来源: https://www.cnblogs.com/qjds/p/16678361.html