靜態庫和動態庫的區別
Spring
1.Spring簡介
使現有的技術更加容易使用,本身是一個大雜燴,整合了現有的所有技術
SSH:Struct2 + Spring + Hibernate
SSM:SpringMVC + Spring + Mybatis
官方文件:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans
官方下載:https://repo.spring.io/ui/native/release/org/springframework/spring/
github地址:https://github.com/spring-projects/spring-framework
<!--maven--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.19</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.19</version> </dependency>
優點:開源免費的容器,輕量級的非入侵式的框架,支援事務的處理,對框架整合的支援
兩大特性:控制反轉(IOC)、面向切面程式設計(AOP)
- Spring Boot:
- 一個快速開發的腳手架
- 基於SpringBoot可以快速開發單個微服務
- 約定大於配置
- Spring Cloud:
- SpringCloud是基於SpringBoot實現的
2.IOC理論
1.基本原理
之前,程式是主動建立物件!控制權在程式手上;使用了set注入後,程式不再具有主動性,而是變成了被動的接受物件
//通過利用介面來達到使用者決定 private UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao = userDao; userDao.test(); }
控制反轉(IOC)是一種設計思想,依賴注入(DI)是實現IOC的一種方法,也有人認為DI只是IOC的另一種說法。沒有IOC的程式中,我們使用面向物件程式設計,物件的建立與物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制,控制反轉後將物件的建立轉移給第三方,可以理解為:獲得依賴物件的方式反轉了。
採用XML方式配置Bean的時候,Bean的定義資訊是和實現分離的,而採用註解的方式可以把兩者合二為一,Bean的定義資訊直接以註解的形式定義在實現類中,從而達到了零配置的目的。
控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定物件的方式。在Spring中實現控制反轉的是IOC容器,其方法是依賴注入(DI)。
本質上是將物件交給Spring託管,由Spring負責建立、管理、裝配。也就是說修改需求只需要在配置檔案中修改,不需要對程式碼做改動。
2.一些配置
- 別名
<alias name="oldName" alias="newName"/>
- bean配置
<!--id為新建的物件名,class為對應的實體類,name可以起別名,同時可以取多個,property對應形參-->
<bean id="oldName" class="com.Gw.pojo.user" name="newName1 newName2,newName3...">
<property name="name" value="yGw"></property>
</bean>
- import
一般用於團隊開發,將多個xml合併為一個applicationContext.xml
<import resource="beans1.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
3.依賴注入
1.構造器注入
在配置檔案載入的時候,容器中所有的物件都已經載入了(呼叫建構函式)
1.預設使用無參構造
2.如果要使用有參構造
- 下標賦值
<bean id="user" class="com.Gw.pojo.User">
<constructor-arg index="0" value="Gw"/>
</bean>
- 型別
<bean id="user" class="com.Gw.pojo.User">
<!--對於引用型別必須指明具體-->
<constructor-arg type="java.lang.String" value="Gw2"/>
</bean>
- 引數名
<bean id="user" class="com.Gw.pojo.User">
<constructor-arg name="name" value="Gw3"/>
</bean>
2.Set方式注入
- 依賴:bean物件的建立依賴於容器!
- 注入:bean物件中的所有屬性由容器來注入!
<!--常見的幾種注入-->
<bean id="address" class="com.Gw.pojo.Address">
<property name="address" value="chengdu"></property>
</bean>
<bean id="student" class="com.Gw.pojo.Student">
<!--第一種,普通注入-->
<property name="name" value="ygw"/>
<!--第二種,Bean注入-->
<property name="address" ref="address"/>
<!--第三種,陣列注入-->
<property name="books">
<array>
<value>紅樓夢</value>
<value>西遊記</value>
<value>水滸傳</value>
<value>三國演義</value>
</array>
</property>
<!--list注入-->
<property name="hobbys">
<list>
<value>聽歌</value>
<value>敲程式碼</value>
<value>打遊戲</value>
<value>刷視訊</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="卡1" value="123456"></entry>
<entry key="卡2" value="123456"></entry>
<entry key="卡3" value="123456"></entry>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>Naraka</value>
<value>ReadyOrNot</value>
<value>CS:GO</value>
</set>
</property>
<!--null注入-->
<property name="wife">
<null/>
</property>
<!--properties注入-->
<property name="info">
<props>
<prop key="學號">202013160322</prop>
<prop key="性別">男</prop>
<prop key="姓名">ygw</prop>
</props>
</property>
</bean>
3.拓展方式注入
p名稱空間注入
<bean id="user" class="com.Gw.pojo.User" p:name="ygw" p:age="20"/>
在使用前需要加入約束
xmlns:p="http://www.springframework.org/schema/p"
c名稱空間注入
<bean id="user2" class="com.Gw.pojo.User" c:name="smart" c:age="21"/>
使用前加入約束
xmlns:p="http://www.springframework.org/schema/p"
4.Bean的作用域
- 單例模式(Spring預設)
<!--將scope設定為singleton,呼叫多次時不重複生成物件-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
- 原型模式
<!--將scope設定為prototype,呼叫時重新生成物件-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
其他的request、session、application、websocket在web中應用
4.Bean的自動裝配
- byName
<!--byName使用時需要保證所有bean的id唯一,並且這個bean需要和自動注入的屬性的set方法的值一致-->
<bean id = "cat" class = "com.Gw.pojo.Cat"/>
<bean id = "dog" class = "com.Gw.pojo.Dog"/>
<bean id = "people" class = "com.Gw.pojo.people" autowire = "byName">
<property name = "name" value = "ygw"/>
</bean>
- byType
<!--byType使用時需要保證所有bean的class唯一,並且這個bean需要和自動注入的屬性的set方法的值一致-->
<!--id可省略-->
<bean class = "com.Gw.pojo.Cat"/>
<bean class = "com.Gw.pojo.Dog"/>
<bean id = "people" class = "com.Gw.pojo.people" autowire = "byName">
<property name = "name" value = "ygw"/>
</bean>
1.註解實現自動裝配
註解注入在XML注入之前執行。因此,XML配置將覆蓋通過註解方法連線的屬性的註釋。
註解使用說明:
配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
2.@Autowired與@Resource
1.聯絡
- @Autowired和@Resource註解都是作為bean物件注入的時候使用的
- 兩者都可以宣告在欄位和setter方法上
注意:如果宣告在欄位上,那麼就不需要再寫setter方法。但是本質上,該物件還是作為set方法的實參,通過執行set方法注入,只是省略了setter方法罷了
2.區別
- @Autowired註解是Spring提供的,而@Resource註解是J2EE本身提供的
- @Autowird註解預設通過byType方式注入,而@Resource註解預設通過byName方式注入
- @Autowired註解注入的物件需要在IOC容器中存在,否則需要加上屬性required=false,表示忽略當前要注入的bean,如果有直接注入,沒有跳過,不會報錯
3.使用
- @Autowird預設的注入方式為byType,也就是根據型別匹配,當有多個實現時,則通過byName注入,也可以通過配合@Qualifier註解來顯式指定name值,指明要使用哪個具體的實現類,如果需要設定物件為空可以用註解@nullable或者將required設定為false
public class People {
@Autowired
@Qualifier(name="cat1")
private Cat cat;
@Autowired
@nullable
private Dog dog;
String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- @Resource首先通過byName匹配,當無法匹配IOC容器中任何一個id,於是通過byType匹配,如果型別的實現類有兩個,仍然無法確定,於是報錯。同時@Resource還有兩個重要的屬性:name和type,用來顯式指定byName和byType方式注入;當指定某種方式匹配時,如果匹配不到則直接報錯
@Resource(name=""/type="")
private Dog dog;
5.使用註解開發
說明:在Spring4之後使用註解必須要aop包,xml更加萬能,維護更加方便;最好是xml用來管理bean,註解只負責屬性的注入
使用註解前需要加入:
<!--包中填寫需要掃描的內容-->
<context:component-scan base-package="com.Gw"/>
<context:annotation-config/>
@Component的衍生註解,@Component可以看做<bean id="" class=""~>
- @Repository -- Dao層
- @Service -- Service層
- @Controller -- Controller層
@Value可以看做<property><value></value~> </property~>
@Scope用來設定作用域@Scope("singleton/prototype/....")
@Component
public class User {
@Value("ygw")
private String name;
public String getName() {
return name;
}
}
6.使用Java方式配置Spring
//Configuration本質上就是Component,@Configuration可以等價於applicationContext.xml
@Configuration
@ConponentScan("com.Gw.pojo") //掃描包
@Import("Myconfig2.class") //引入其他包
public class Myconfig {
//等價於<bean id = "getUser" class = "com.Gw.pojo.User"/>
@Bean
@Value("ygw")
public User getUser(){
return new User();
}
}
//如果完全使用了配置類去做,則配置的使用通過AnnotationConfigApplicationContext獲取上下文
@Test
public void test1(){
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
}
7.動態代理
動態代理主要會用到Proxy和InvocationHandler
- InvocationHandler(動態代理)
//InvocationHandler是一個介面,定義了invoke方法,invoke是生成proxy例項的方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- Proxy(代理人)
Modifier and Type | Method and Description |
---|---|
static InvocationHandler |
getInvocationHandler(Object proxy) Returns the invocation handler for the specified proxy instance. |
static Class<?> |
getProxyClass(ClassLoader loader, Class<?>... interfaces) Returns the java.lang.Class object for a proxy class given a class loader and an array of interfaces. |
static boolean |
isProxyClass(Class<?> cl) Returns true if and only if the specified class was dynamically generated to be a proxy class using the getProxyClass method or the newProxyInstance method. |
static Object |
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler. |
舉例說明,在原有業務UserService的基礎上加上日誌輸出
package com.Gw.demo01;
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(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//最最最最重要的方法,在使用getProxy()方法時,給出的InvocationHandler是this,也就是說代理人在動態的執行userService中的方法時會呼叫當前類的invoke方法,在這個方法中用method.invoke()來呼叫代理物件的方法
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
log(method.getName());
Object result = method.invoke(target, objects);
return result;
}
public void log(String msg){
System.out.println("[debug] 執行了" + msg +"方法");
}
}
import com.Gw.demo01.ProxyInvocationHandler;
import com.Gw.demo01.UserService;
import com.Gw.demo01.UserServiceImpl;
//使用動態代理
public class test {
public static void main(String[] args) {
//先new出需要代理的物件userService
UserServiceImpl userService = new UserServiceImpl();
//將userService放入動態代理器中,並獲取代理人proxy
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(userService);
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
//通過代理人進行操作
proxy.delete();
proxy.add();
}
}
8.AOP
提供宣告式事務;允許使用者自定義切面
使用Spring-AOP需要匯入依賴
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
<scope>runtime</scope>
</dependency>
1.一些術語的說明
- 橫切關注點:跨越應用程式的多個模組的方法或功能,與業務邏輯無關,但需要關注的部分,如:日誌、安全、快取、事務等等
- 切面(aspect):橫切關注點被模組化的特殊物件,如log類
- 通知(advice):切面必須要完成的工作,也就是log類中的一個方法
- 目標(target):被通知的物件
- 代理(proxy):向目標物件應用通知後建立的物件
- 切入點(pointCut):切面通知執行的地點
- 連線點(joinPoint):與切入點匹配的執行點
2.Spring中AOP的三種實現方法
方法一:使用Spring中的API
public class log implements MethodBeforeAdvice {
//method:要執行的方法
//args:引數
//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);
}
}
在xml中的配置
<!--方法一:未自定義切面-->
<!--配置AOP,需要匯入AOP的約束-->
<aop:config>
<!--定義一個切入點,execution寫切入點位置,*代表任意位置-->
<aop:pointcut id="pointcut" expression="execution(* com.Gw.demo01.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
</aop:config>
方法二:自定義切面
public class diyAspect {
public void before(){
System.out.println("=========before=========");
}
public void after(){
System.out.println("=========after==========");
}
}
在xml中的配置
<!--方法二:自定義切面-->
<bean id="diyaspect" class="com.Gw.diy.diyAspect"/>
<aop:config>
<!--自定義切面-->
<aop:aspect ref="diyaspect">
<!--設定切入點-->
<aop:pointcut id="point" expression="execution(* com.Gw.demo01.UserServiceImpl.*(..))"/>
<!--通知,可直接呼叫切面中的方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方法三:使用註解實現
package com.Gw;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //設定切面
public class Annotation {
//設定切入點和通知
@Before("execution(* com.Gw.demo01.UserServiceImpl.*(..))")
public void before(){
System.out.println("=========before=========");
}
@After("execution(* com.Gw.demo01.UserServiceImpl.*(..))")
public void after(){
System.out.println("=========after==========");
}
@Around("execution(* com.Gw.demo01.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞前");
Object proceed = jp.proceed();
System.out.println("環繞後");
}
}
9.整合Mybatis
用spring的配置取代mybatis-config.xml的配置,通常只在mybatis-config.xml中配置別名管理和設定
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--做別名管理和設定-->
<typeAliases>
<package name="com.Gw.pojo"/>
</typeAliases>
</configuration>
匯入的依賴:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.19</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
方法一:直接實現
先建立一個mapper,在mapper.xml中實現方法,在原有的mybatis-config.xml的基礎上進行替換,配置的spring-dao.xml如下,將SqlSession利用spring注入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--DataSource:使用Sping的資料來源替代Mybatis的配置-->
<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?useSSL=false&userUnicode=true&characterEncoding=UTF-8"/>
<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"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/Gw/Mapper/*.xml"/>
</bean>
<!--只能用構造器注入,因為template沒有set方法 -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--將實現類注入到Spring中-->
<bean id="userMapper" class="com.Gw.Mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
<bean id="userMapper2" class="com.Gw.Mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
將sqlSession利用spring進行注入時,要求了一開始的mapper介面需要一個實現類,和sqlSession的set方法:
package com.Gw.Mapper;
import com.Gw.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
//我們所有的操作現在都使用SqlSessionTemplate來執行
private SqlSessionTemplate sqlSession;
//spring注入要求必須有set方法
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
return sqlSession.getMapper(UserMapper.class).selectUser();
}
}
最後使用applicationContext.xml整合spring中的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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">
<import resource="spring-dao.xml"/>
<!--將實現類注入到Spring中-->
<bean id="userMapper" class="com.Gw.Mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
</beans>
方法二:SqlSessionDaoSupport簡便實現
在一的配置的基礎上繼承SqlSessionDaoSupport,省去了建立sqlSession物件的麻煩,sqlSession物件直接由getSession()方法獲得,對應的mapper實現類如下:
package com.Gw.Mapper;
import com.Gw.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
//直接通過getSqlSession獲得物件
@Override
public List<User> selectUser() {
SqlSession sqlSession = getSqlSession();
return sqlSession.getMapper(UserMapper.class).selectUser();
}
}
10.宣告式事務
把一組業務當做一個業務來開發,要麼都成功,要麼都失敗,確保完整性和一致性,spring 保證的是在不改變原有程式碼的基礎上進行事務注入,非常的nice
事務的ACID原則:
原子性、一致性、隔離性、永續性
新增配置tx
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
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/context/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-aop.xsd">
要開啟spring 的事務管理功能需要在spring的配置檔案中建立一個DataSourceTransactionManager物件
<!--結合AOP實現事務的織入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--給哪些方法配置事務-->
<!--配置事務的七種傳播特性-->
<tx:attributes>
<tx:method name="selectUser" read-only="true"/>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete"/>
<tx:method name="update"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事務切入點-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.Gw.Mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>