Spring5完整版詳解

来源:https://www.cnblogs.com/twq46/archive/2022/08/17/16580569.html
-Advertisement-
Play Games

Spring5相關的知識點,IOC,AOP,DI依賴註入,Bean的自動裝配,使用註解開發,spring整合mybatis等於Spring相關的知識都在這裡了 ...


1、Spring

1.1簡介

  • 2002,首次退出來Spring框架的雛形:interface21框架

  • Spring框架即以interface21框架為基礎,經過重新設計,並不斷豐富其內涵,與2004年3月24日,發佈了1.0正式版

  • Rod Johnson,Spring framework創始人,著名作者。

  • spring理念:是現有的技術更加容易使用,本身是一個大雜燴

官網地址:

https://spring.io/projects/spring-framework

官方下載地址
https://repo.spring.io/ui/native/release/org/springframework/spring/

對應jar包的導入

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

1.2優點

  • Spring是一個開源的免費的框架(容器)

  • Spring是一個輕量級的,非入侵式的框架(不會使項目因為導入改jar包而導致項目不能運行)

  • 控制反轉(IOC),面向切麵編程(AOP)

  • 支持事物的處理,對框架整合的支持

總結一句話:Spring就是一個輕量級的控制反轉(IOC)和麵向切麵編程(AOP)的框架!

1.3組成

1.4拓展

在Spring的官網有這個介紹:現代化的Java開發!說白了就是基於Spring的開發

  • Spring Boot

    • 一個快速開發的腳手架(用了SpringBoot之後就可以根據一個配置就可以開發一個網站)
    • 基於SpringBoot可以快速的開發單個微服務
    • 約定大於配置
  • Spring Cloud

    • SpringCloud是基於SpringBoot實現的

因為現在大多數公司都在使用SpringBoot進行快速開發,學習SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上啟下的作用

弊端:發展了太久之後,違背了原來的理念,配置十分繁瑣,人稱:“配置地獄”

2.IOC理論推導

2.1以前寫項目的過程

  • UserDao介面
package com.tang.dao;

public interface UserDao {
    public void getUser();
}
  • UserDaoImpl實現類
package com.tang.dao;

public class UserDaoImpl implements UserDao{
    public void getUser() {
        System.out.println("預設UserDao載入數據");
    }
}

UserMysqlDaoImpl實現類

public class UserMysqlDaoImpl implements UserDao{
    public void getUser() {
        System.out.println("Mysql獲取數據");
    }
}
  • UserService業務介面
package com.tang.service;

public interface UserService {
    public void getUser();
}

  • UserServiceImpl業務實現類
package com.tang.service;

import com.tang.dao.UserDao;
import com.tang.dao.UserDaoImpl;

public class UserServiceImpl implements UserService{

    //private UserDao userDao = new UserDaoImpl();

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    //利用set進行動態實現指的註入
    public void getUser() {
        userDao.getUser();
    }
}

模擬的用戶層

public class Mytest {
    @Test
    public void test(){
        //用戶實體調用的是業務層,dao層他們不需要接觸
        UserService userService = new UserServiceImpl();
        //控制權交給用戶,用戶想調用誰的實現類就直接new即可
        ((UserServiceImpl)userService).setUserDao(new UserMysqlDaoImpl());
        userService.getUser();
    }
}

測試結果

在我們之前的業務中,用戶的需求可能會影響我們原來的代碼,我們需要根據用戶的需求去修改源代碼,如果程式代碼量十分大,修改一次的成本代價十分昂貴

我們使用一個set介面實現,每當用戶更改之後,不用在業務層去修改源碼,只需要在用戶層,根據用戶所需調用相應的service層然後service層去調用dao層的代碼

public class UserServiceImpl implements UserService{
    //以前就需要在業務層,每當用戶改變之後new 的實現類也必須隨之發生變化,加了個set之後就可以在用戶層根據所需通過service間接使用所需要的用戶層的實現類
    //private UserDao userDao = new UserDaoImpl();

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    //利用set進行動態實現指的註入
    public void getUser() {
        userDao.getUser();
    }
}
  • 之前,程式是主動創建對象,控制權在程式猿手上

  • 使用set註入後,程式不再具有主動性,而是變成了被動的接收對象

這種思想,從本質上解決了問題,我們程式員不用再去管理對象的創建了,系統的耦合性大大降低,可以更加專註在業務的是線上,這是IOC的原型

2.2.IOC本質

控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴註入)是實現IoC的一種方法,也有人認為DI只是IoC的另一種說法。沒有IoC的程式中 , 我們使用面向對象編程 , 對象的創建與對象間的依賴關係完全硬編碼在程式中,對象的創建由程式自己控制,控制反轉後將對象的創建轉移給第三方,個人認為所謂控制反轉就是:獲得依賴對象的方式反轉了。

採用XML方式配置Bean的時候,Bean的定義信息是和實現分離的,而採用註解的方式可以把兩者合為一體,Bean的定義信息直接以註解的形式定義在實現類中,從而達到了零配置的目的。

控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定對象的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴註入(Dependency Injection,DI)。

3.HelloSpring

bean.xml創建方式


實體類User

package com.tang.pojo;

public class Hello {
    private String str;

    public Hello() {
    }

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

    public String getStr() {
        return str;
    }

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

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

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--bean = 對象-->
    <!--id = 變數名-->
    <!--class = new的對象-->
    <!--property 相當於給對象中的屬性設值-->
    <bean id="hello" class="com.tang.pojo.Hello">
            <property name="str" value="Spring"></property>
        </bean>
</beans>

測試代碼

public class MyTest {
    @Test
    public void test(){
        //獲取Spring的上下文對象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我們的對象現在都在Spring中管理了,我們要使用,直接去裡面取出來就可以了
        Hello hello = (Hello) context.getBean("hello");//這裡就相當於new了一個對象

        System.out.println(hello.toString());
    }
}

運行結果圖

如果運行之後報這個錯IOException parsing XML document from class path resource [beans.xml]; nested exception is java.io.FileNotFoundException: class path resource [beans.xml] cannot be opened because it does not exist
可以將該項目的pom文件的打包方式設置為jar即可解決此問題,問題出現原因就是在target中沒有beans.xml文件導致

【思考問題?】

  • Hello對象是誰創建的?
    hello對象是由Spring創建的

  • Hello對象的屬性是怎麼設置的?

    • hello對象的屬性是由Spring容器設置的

這個過程就叫控制反轉:

控制:誰來控制對象的創建,傳統應用程式的對象是有程式本身控制創建的,使用Spring後,對象是由Spring來創建的

反轉:程式本身不創建對象,而變成不被動的接收對象

依賴註入:就是利用set方法來進行註入的

IOC是一種編程思想,由主動地編程變成被動的接收

可以通過new ClassPathXmlApplicationContext去瀏覽以下底層的源碼

OK,到了現在,我們徹底不用在程式中去改動了,要實現不同的操作,只需要在xml配置文件中進行修改,所謂的IOC一句話搞定:對象有Spring來創建,管理,裝配!

4.IOC創建對象的方式

4.1使用無參構造創建對象,預設

beans.xml

<!--這裡就相當於new了一個對象,不管用於不用,這裡都已經創建了一個對象-->
<bean id="user" class="com.tang.pojo.User">
    <property name="name" value="唐昊"></property>
</bean>

測試代碼

@Test
public void UserTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user");
}

測試結果

4.2假設我們要使用了有參構造

下標賦值

<bean id="user" class="com.tang.pojo.User">
    <constructor-arg index="0" value="User的有參構造"/>
</bean>

通過類型創建,不建議使用

 <bean id="user" class="com.tang.pojo.User">
        <constructor-arg type="java.lang.String" value="User有參構造"></constructor-arg>
    </bean>

通過參數名來設置

<bean id="user" class="com.tang.pojo.User">
    <constructor-arg name="name" value="唐昊"></constructor-arg>
</bean>

5.Spring配置

5.1別名

<!--別名,如果添加了別名,我們也可以使用別名獲取到這個對象-->
<alias name="user" alias="fasdagsd"></alias>
@Test
public void UserTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("fasdagsd");
    System.out.println(user.toString());
}

5.2Bean的配置

<!--
id:bean的唯一標識符,也就是相當於我們學的對象名
class: bean對象多對應的許可權定名:包名 + 類型
name:也是別名 而且name也可以同時取多個別名,每個別名之間可以用逗號,空格,分號等符號都可以實現,name起別名還是很強大的
-->
<bean id="user" class="com.tang.pojo.User" name="user2,twq">
    <constructor-arg name="name" value="唐昊"></constructor-arg>
</bean>

5.3Import

這個import,一般用於團隊開發使用,他可以將多個配置文件,導入合併一個

假設,現在項目中有多個人開發(張三,李四,王五),這三個人複製不同的類開發,不同的類需要註冊在不同的Bean中,我們可以利用import將多誘人的beans.xml合併為一個總的

使用的時候,直接使用總的配置就可以了
項目結構圖

application.xml

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

6.DI依賴註入

6.1Set方式註入(重點)

  • 依賴註入:Set註入
    • 依賴:bean對象的創建依賴於容器
    • 註入:bean對象中的所有屬性,由容器註入

(1)環境搭建

複雜類型
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> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
  //get,set方法這裡就不寫上了,可以自己寫上
}
beans.xml
<bean id="student" class="com.tang.pojo.Student">
    <!--第一種,普通的註入,value-->
    <property name="name" value="唐昊"></property>

</bean>
測試類
@Test
public  void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Student student = (Student) context.getBean("student");
    System.out.println(student.getName());

}

完善註入信息
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="address" class="com.tang.pojo.Address">
        <property name="address" value="北京"></property>
    </bean>

    <bean id="student" class="com.tang.pojo.Student">
        <!--第一種,普通的註入,value-->
        <property name="name" value="唐昊"></property>

        <!--第二種,Bean註入,使用ref-->
        <property name="address" ref="address"></property>

        <!--第三種:數組的註入-->
        <property name="books">
            <array>
                <value>紅樓夢</value>
                <value>三國</value>
                <value>水滸傳</value>
            </array>
        </property>

        <!--第四種:list的註入-->
        <property name="hobbys">
            <list>
                <value>聽歌</value>
                <value>敲代碼</value>
                <value>看電影</value>
            </list>
        </property>

        <!--第五種:map的註入-->
        <property name="card">
            <map>
                <entry key="身份證" value="1111111111111111111"></entry>
                <entry key="銀行卡" value="33333333333333333333"></entry>
            </map>
        </property>

        <!--第六種:Set的註入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>

        <!--第七種:null的註入-->
        <property name="wife">
            <null></null>
        </property>

        <!--第八種:Properties-->
        <property name="info">
            <props>
                <prop key="學號">20220140</prop>
                <prop key="性別">男</prop>
                <prop key="學號">20220140</prop>
            </props>
        </property>
    </bean>
</beans>

測試結果

7.Bean的自動裝配

7.1自動裝配

自動裝配是Spring滿足Bean依賴的一種方式

spring會在上下文中自動尋找,並自動給Bean裝配屬性

在Spring中有三種裝配的方式

  • 在xml中顯示的配置

  • 在Java中顯示配置

  • 隱式的自動裝配Bean【重要】

7.2測試

環境搭建:一個人有兩個寵物

ByName自動裝配

<!--byName:會自動在容器上下文中查找,和自己對象set方法後面的值對應的beanid-->
<bean id="people" class="com.tang.pojo.People" autowire="byName">
    <property name="name" value="twq"></property>
<!--<property name="dog" ref="dog"></property>-->
<!--<property name="cat" ref="cat"></property>-->
</bean>

ByType自動裝配

<bean id="cat" class="com.tang.pojo.Cat"></bean>
    <bean id="dog111" class="com.tang.pojo.Dog"></bean>
<!--
byType:會自動在容器上下文中查找,和自己對象屬性類型相同的bean !
-->
<bean id="people" class="com.tang.pojo.People" autowire="byType">
    <property name="name" value="twq"></property>
<!--<property name="dog" ref="dog"></property>-->
<!--<property name="cat" ref="cat"></property>-->
</bean>

小結:

  • byName的時候,需要保證所有bean的id唯一,並且這個bean需要和自動註入的屬性的set方法的值一致

  • byType的時候,需要保證所有bean的class唯一,並且這個bean需要和自動註入的屬性的類型一致

7.3使用註解實現自動裝配

要使用註解須知:
  • 導入約束: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></context:annotation-config>

    <bean id="cat" class="com.tang.pojo.Cat"></bean>
    <bean id="dog111" class="com.tang.pojo.Dog"></bean>
    <bean id="people" class="com.tang.pojo.People"></bean>

</beans>

在實體類上使用註解即可

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}

測試代碼

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    People people = context.getBean("people", People.class);
    people.getDog().shot();
    people.getCat().shot();
}

運行結果圖

@Autowired

直接在屬性山使用即可,也可以在set方法上使用

使用@Autowired我們可以不用編寫set方法了,前提是你這個自動裝的屬性在IOC(Spring)容器中存在,且符合名字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></context:annotation-config>
</beans>

8.1.屬性如何註入

//等價於<bean id="user" class="com.tang.pojo.User"></bean>
//@Component組件,放在類上,說明這個類被Spring管理了,就是bean
@Component
public class User {
//    public String name ="唐昊";
    //相當於 <property name="name" value="唐昊"/>
    @Value("唐昊")//給name賦值
    public String name;

}

8.2衍生的註解

@Component有幾個衍生註解,我們在web開發中,會按照mvc三層架構分層

  • dao【@Repository】

  • service【@Service】

  • controller【@Controller】
    這四個註解功能都是一樣的,都是代表某個類註冊到Spring中,裝配Bean

8.3小結

xml與註解:
  • xml更加萬能,適用於任何場合!維護簡單方便

  • 註解不是自己的類使用不了,維護相對複雜

最佳實踐:

  • xml用來管理bean

  • 註解只負責完成屬性的註入

  • 我們在使用的過程中,只需要註意一個問題,必須讓註解生效,就需要開啟註解的支持

    <!--指定要掃描的包,這個包下的註解就會生效-->
    <context:component-scan base-package="com.tang.pojo"></context:component-scan>
    
    <context:annotation-config></context:annotation-config>
    

9.使用Java的方式配置Spring

我們現在要完全不使用Spring的xml配置了,全權交給java來做

JavaConfig是Spring的一個子項目,在Spring4之後,他就成為了一個核心功能

User實體類代碼

//這裡這個註解的意思,就是說明這個類被Spring接管了,註冊到了容器中
@Component
public class User {
    @Value("唐三")//將name的值設置為唐三
    private String name;

    public String getName() {
        return name;
    }

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

Config類

/*
* 這個也會被Spring容器托管,註冊倒容器中,因為他本愛就是一個@Component
* @Confguration代表這是一個配置類,就是我們之前看的beans.xml*/
@Configuration
@Component("com.tang.pojo")
public class TangConfig {

//註冊一個bean,就相當於我們之前寫的一個bean標簽
//這個方法的名字,就相當於bean標簽的id屬性
//這個方法的返回值,就相當於bean標簽中class屬性
@Bean
public User getUser(){
    return new User();//就是要返回要註入到bean的對象
}

測試類

public class MyTest {
    @Test
    public void test(){
        //如果完全使用了配置類方式去做,我們就只能通過AnnotationConfig上下文
        // 來獲取容器,通過配置類的class對象載入
        ApplicationContext context = new AnnotationConfigApplicationContext(TangConfig.class);
        User getUser = context.getBean("getUser", User.class);//取得getUser是TangConfig中經過Bean註冊過的

        System.out.println(getUser.getName());

    }

這種純java的配置方式,在SpringBoot中隨處可見

10.代理模式

為什麼要學習代理模式? 因為這就是SpringAOP的底層!【SpringAOP和SpringMVC面試必問】

代理模式的分類:

  • 靜態代理

  • 動態代理

10.1靜態代理

角色分析:
  • 存在一個抽象角色(比如上圖中介和房東共同完成租房這個事件):一般會使用介面或者抽象類來解決

  • 真是角色:被代理的角色

  • 代理角色:代理真是角色,代理真實角色後,我們一般會做一些附屬操作

  • 客戶:訪問代理對象的人

代碼步驟:

①.介面

//租房
public interface Rent {

   public void rent();
}

②.真實角色(房東)

//房東
public class Host implements Rent {
    public void rent(){
        System.out.println("房東要出租房子");
    }
}

③.代理角色

//代理(中介幫房東出租房子)
public class Proxy implements Rent{
    private Host host;//中介得找到房東

    public Proxy() {
    }

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

    public void rent() {
        host.rent();
        seeHouse();
        contract();
        fees();
    }

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

    //簽合同
    public void contract(){
        System.out.println("簽租賃合同");
    }

    //收中介費
    public void fees(){
        System.out.println("收中介費");
    }
}

④.客戶訪問dialing角色

public class Client {
    public static void main(String[] args) {
        Host host = new Host();//房東要出租房子
        //中介幫房東出租房子,但是dialing角色一般會有一些附屬操作
        Proxy proxy = new Proxy(host);

        //你不用面對房東,直接找中介租房
        proxy.rent();
    }
}

代理模式的好處

  • 可以使真實角色的操作更加純粹!不用去關註一些公共的業務

  • 公共業務就交給代理角色!實現業務的分工

  • 公共業務發生擴展的時候,方便集中管理

缺點:

  • 一個真是角色就會產生一個代理角色;代碼量會翻倍-開發效率低

10.2加深理解

目的:在每次調用增刪改查的時候列印一個日誌,表明當前調用的是哪個操作,在公司里在原來的代碼上修改是大忌,這時就可以採用代理模式來實現

UserService介面

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

UserServiceImpl代碼

public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("添加了一個用戶");
    }

    public void delete() {
        System.out.println("刪除了一個用戶");
    }

    public void update() {
        System.out.println("修改了一個用戶");
    }

    public void query() {
        System.out.println("查詢了一個用戶");
    }
}

UserServiceProxy代理代碼

public class UserServiceProxy implements UserService{

    private UserServiceImpl userServiceimpl;

    public void setUserServiceimpl(UserServiceImpl userServiceimpl) {
        this.userServiceimpl = userServiceimpl;
    }

    public void add() {
        log("add");
        userServiceimpl.add();
    }

    public void delete() {
        log("delete");
        userServiceimpl.delete();
    }

    public void update() {
        log("update");
        userServiceimpl.update();
    }

    public void query() {
        log("query");
        userServiceimpl.query();
    }

    public void log(String name){
        System.out.println("[debug]調用了"+name+"方法");
    }
}

測試代碼

public class MyTest {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserServiceimpl(userService);

        userServiceProxy.add();
    }
}

測試結果

10.3動態代理

  • 動態代理和靜態代理角色一樣

  • 動態代理的代理類是動態生成的,不是我們直接寫好的

  • 動態dialing分為兩大類:基於介面的代理,基於類的動態代理

    • 基於介面---JDK動態代理
    • 基於類:cglib
    • java位元組碼實現:javAssist

需要瞭解兩個類:Proxy:代理,InvocationHandler:調用處理程式
租房介面類

//租房
public interface Rent {

   public void rent();
}

真實角色Host房東

//房東
public class Host implements Rent {
    public void rent(){
        System.out.println("房東要出租房子");
    }
}

動態生成代理ProxyInvocationHandler

//等會這裡會用這個類,自動生成代理類
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);
    }

    //處理代理實例,並返回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //動態代理的本質,就是使用反射機制實現
        Object result = method.invoke(rent, args);
        contract();
        return result;
    }

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

    //簽合同
    public void contract(){
        System.out.println("簽租賃合同");
    }

}

用戶類

public class Client {
    public static void main(String[] args) {
        //真實角色
        Host host = new Host();

        //代理角色:現在沒有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通過調用程式處理鴃舌來處理我們要調用的介面對象
        pih.setRent(host);

       Rent proxy = (Rent) pih.getProxy();//這裡的Proxy就是動態生成的,我們並沒有寫

       proxy.rent();
    }
}

測試結果


將ProxyInvocationHandler提取為工具類,並實現增刪改查的時候列印日誌的操作
項目結構圖


ProxyInvocationHandler代碼

package com.tang.demo04;


import com.tang.demo03.Rent;

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

//等會我們會用這個類,自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的介面
    private Object target;

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

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

    //處理代理實例,並返回結果
    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.delete();

    }
}

測試結果

動態代理的好處:

  • 可以使真實角色的操作更加純粹!不用去關註一些公共的業務

  • 公共業務就交給代理角色!實現業務的分工

  • 公共業務發生擴展的時候,方便集中管理

*一個動態代理類處理的是一個介面,一般就是對應的一類業務

  • 一個動態代理類可以代理多個類,只要是實現類同一個介面即可

11.AOP

11.1什麼是AOP

在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

11.2AOP在Spring中的作用

提供聲明式事務;允許用戶自定義切麵

11.3使用Spring實現Aop

【重點】使用AOP織入,需要導入一個依賴包
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

①使用Spring的API介面

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

UserServiceImpl代碼

public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("添加了一個用戶");
    }

    public void delete() {
        System.out.println("刪除了一個用戶");
    }

    public void update() {
        System.out.println("修改了一個用戶");
    }

    public void select() {
        System.out.println("查詢了一個用戶");
    }
}

Log代碼

public class Log implements MethodBeforeAdvice {
    //method:要執行的目標對象的方法
    //args:參數
    //target:目標對象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
    }
}

AfterLog代碼

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("執行了"+method.getName()+"方法,返回值結果為:"+returnValue);
    }
}

applicationContext.xml代碼

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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-->
    <bean id="userService" class="com.tang.service.UserServiceImpl"></bean>
    <bean id="log" class="com.tang.log.Log"></bean>
    <bean id="afterLog" class="com.tang.log.AfterLog"></bean>
    <!--方式一:使用原生Spring API介面 -->
    <!--配置aop:需要導入aop的約束-->
    <aop:config>
        <!--切入點:expression:表達式,execution(需要執行的位置)-->
        <aop:pointcut id="pointcut" expression="execution(* com.tang.service.UserServiceImpl.*(..))"/>
        <!--執行環繞增強-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

測試代碼

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //動態代理代理的是介面
        UserService userService = (UserService) context.getBean("userService");

        userService.delete();
    }
}

測試結果

②自定義來實現AOP

diy下的DiyPointCut類的代碼
public class DiyPointCut {

    public void before(){
        System.out.println("=======方法執行前======");
    }

    public void after(){
        System.out.println("=======方法執行後======");
    }
}

applicationContext.xml核心代碼

    <!--方式二:自定義類-->
    <bean id="diy" class="com.tang.diy.DiyPointCut"/>
    <aop:config>
        <aop:aspect ref="diy">
            <!--切入點-->
            <aop:pointcut id="point" expression="execution(* com.tang.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

其它代碼同方式一使用Spring的API介面代碼相同就不在寫了
測試結果

③使用註解來實現AOP

beas.xml核心代碼
<!--方式三:使用註解實現AOP-->
<bean id="annotationPoinCut" class="com.tang.diy.AnnotationPointCut"/>
<!--開啟註解支持 JDK(預設 proxy-target-class="false")  cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy />

AnnotationPointCut代碼

//方式三:使用註解方式實現AOP
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.tang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=======方法執行前======");

    }
    @After("execution(* com.tang.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=========方法執行後==========");
    }

    //在環繞增強中,我們可以給定一個參數,代表我們要獲取處理切入的點
    @Around("execution(* com.tang.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("環繞後");

        System.out.println(proceed);

    }
}

測試結果

12.整合Mybatis

12.1步驟

①導入相關jar包

  • junit

  • mybatis

  • mysql資料庫

  • spring相關的

  • aop織入

  • mybatis-spring

②編寫配置文件

③測試

12.2回憶mybatis

步驟:
  • 編寫實體類
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
  • 編寫核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--引入外部配置文件-->
<!--    <properties resource="db.properties"/>-->
    <!--可以給實體類起別名-->
    <typeAliases>
        <package name="com.tang.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--                &amp;在xml文件中與符號需要這樣來轉義-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.tang.mapper.UserMapper"></mapper>
    </mappers>

</configuration>
  • 編寫介面
    UserMapper介面代碼
public interface UserMapper {
    public List<User> selectUser();
}
  • 編寫mapper.xml
<!--核心配置文件-->
<mapper namespace="com.tang.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>
</mapper>
  • 測試
public class MyTest {
    @Test
    public void test() throws IOException {
        String resources ="mybatis-config.xml";
       InputStream in = Resources.getResourceAsStream(resources);
       SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
       SqlSession sqlSession = sessionFactory.openSession(true);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.selectUser();


        for (User user : userList) {
           System.out.println(user);
        }
    }
}

測試結果:

12.2Mybatis-spring

項目結構圖

①編寫數據源配置

<!--DataSource:使用Spring的數據源替換Mybatis的配置 c3p0 dbcpp druid
我們這裡使用Spring的JDBC
-->
<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?serverTimezone=Asia/Shanghai&amp;useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="root123456"/>
</bean>

②sqlSessionFactory

<!--slqSessionFactory-->
<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/tang/mapper/*.xml"/>
</bean>

③sqlSessionTemplate

<!--SqlSessionTemplate:就是我們使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用構造器註入sqlSessionFactory,因為它沒有set方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

④需要給介面加實現類

public class UserMapperImpl implements UserMapper{

    //我們的所有操作,都使用sqlSession來執行,在原來,現在都使用SqlSessionTemplate;
    private SqlSessionTemplate sqlSession;

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

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

⑤將自己寫的實現類,註入到Spring中

<bean id="userMapper" class="com.tang.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

⑥測試使用即可

public class MyTest {
    @Test
    public void test() throws IOException {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");

        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

測試結果


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 發佈和訂閱模式(Pub/Sub)是構建企業級 .NET 應用程式不可或缺的工具。Pub/Sub 是一種消息傳遞範式,消息的發送方(發佈者)不知道目標接收方(訂閱者)的任何信息。此外,發佈者和訂閱者之間不直接發生交互,而是依賴一種叫作主題的公共媒介。因此,這是一個鬆散耦合的消息模型。 現在,我們假設在... ...
  • Oracle 備份與恢復 (Docker部署版) 一,宿主機設置定時備份腳本 1.檢查Oracle容器是否正常運行 docker ps 2.進入容器,創建shell腳本 #oracle11g 是容器名 docker exec -it oracle11g bash #創建資料庫dump的目錄 mkdi ...
  • StoneDB 的整體架構分為三層,分別是應用層、服務層和存儲引擎層。應用層主要負責客戶端的連接管理和許可權驗證;服務層提供了 SQL 介面、查詢緩存、解析器、優化器、執行器等組件;Tianmu 引擎所在的存儲引擎層是 StoneDB 的核心,數據的組織和壓縮、以及基於知識網格的查詢優化均是在 Tia ...
  • 1.概述 說起垃圾收集(Garbage Collection, GC),大部分人都把這項技術當做Java語言的伴生產物。事實上,GC的歷史遠遠比Java久遠,1960年誕生於MIT的Lisp是第一門 真正使用記憶體動態分配和垃圾收集技術的語言。當Lisp還在胚胎時期時,人們就在思考 GC需要完成的三件 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 ...
  • 1. 運行時數據區域 Java虛擬機在執行Java程式的過程中會把它所管理的記憶體劃分為若幹個不同的數據 區域。這些區域都有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的 啟動而存在,有些區域則是依賴用戶線程的啟動和結束而建立和銷毀。根據《Java虛擬 機規範(第2版)》的規定,Java虛 ...
  • Java集合05 11.HashSet課堂練習 11.1課堂練習1 定義一個Employee類,該類包括:private成員屬性name,age 要求: 創建3個Employee對象放入HashSet中 當name和age的值相同時,認為是相同員工,不能添加到HashSet集合中 思路:不同對象的哈 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...