spring知識點總結
spring簡介
Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,Spring的核心是控制反轉(IoC)和麪向切面(AOP)
(核心一)IOC控制反轉
控制反轉就是將控制權從程序員手中轉到了用戶手中,降低程序耦合度,簡單來說就是一個程序裏聲明另一個類作爲屬性,這個屬性就是固定死了,IOC就數將每個聯合的類交個spring來管理(個人理解),也就是對象的創建交給第三方。
DI依賴注入
DI依賴注入是實現IOC的一種方法,因爲實現的好,普遍認爲DI就是IOC。
- IOC實現方法
- xml:下面講的是xml形式的,通過ClassPathXMLApplicationContext(“xml文件”)獲得上下文。
- javaconfig:通過AnnotationConfigApplicationContext(java類對象)獲得上下文。
(核心二)AOP面向切面編程
AOP爲(Aspect Oriented Programming)的縮寫,意爲面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容。學過安卓開發的同學就知道使用ASM插件也是AOP思想的實現方式之一。
首先先要了解設計模式——代理模式
-
代理模式
-
靜態代理
使用代理類操作,不直接接觸功能類,但是同樣實現同一個接口。
-
動態代理
利用java反射機制減少代碼量。
-
正文
初始spring基礎——創建spring項目步驟(IOC)
-
使用maven創建spring項目
首先用maven導入依賴,不清楚maven可以先自行了解maven知識。
<!--pom.xml--> <!--這裏只是添加其中一個依賴的建議,具體可以看spring官網,下載來的依賴一般放在默認的.m2下的repository文件夾裏面--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies>
-
創建項目
-
在java包裏面的pojo(自己創建)文件夾創建實體類User,給一個name屬性。
-
在resource裏面創建applicationContext.xml(名字隨便取,這是官方推薦名,我下文叫它beans.xml文件)文件配置beans(spring是一個容器,裏面的東西叫做bean)。
<!--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.qjl.demo.pojo.User"> <property name="name" value="QJL"/> </bean> </beans> <!--上面除了value還可以是ref,意爲配置好的對象,一般用於在該文件配置好的非基本數據類型的對象。-->
-
構造器選擇
<!--有參構造器三種使用方法如下,index是參數的順序,ref找的是註冊過的bean的ID--> <bean id="User" class="com.qjl.demo.pojo.User"> <constructor-arg index="0" value="Good"/> <constructor-arg index="1" ref="OtherBean"/> </bean> <!--使用類型匹配,必須參數類型都互不相同的情況下使用--> <bean id="User" class"com.qjl.demo.pojo.User"> <constructor-arg type="String" value="QJL"/> </bean> <!--推薦以下方法直接使用name指定參數名稱--> <bean id="User" class="com.qjl.demo.pojo.User"> <constructor-arg name="name" value="QJL"/> </bean>
-
到這裏注意下
注意點:想要成功配置一個bean,而該bean中有屬性,則一定要設置setter方法spring容器就是靠setter方法來注入的。
注意點:IDEA默認寫XML時會有提示,所以要是沒有提示就是沒有配置上下文,在項目結構裏面找到模塊,找到自己的模塊展開,找到spring,再在最右邊的區域添加上下文(左上角有個加號的)。
-
實例化xml文件,獲取上下文對象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //這個對象可以將xml文件實例化,另外參數可以傳入多個xml文件,同時這個這一步會將所 有註冊的類new出來,調用的是無參構造器。 User user = (User)context.getBean("User"); //獲得Object對象後轉化一下就可以獲得User對象,bean裏面的參數傳上面設置的bean的Id值,這一個方法始終只能獲得一個示例。
-
如果有報log4j的錯在resource文件夾裏面創建log4j.properties文件並複製以下代碼進去。
log4j.rootLogger=DEBUG,stdout ### console ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[${projectName}] [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %n%m%n ###複製進去先再說,是用來生成log信息的配置,以後用到再研究###
-
beans文件其他配置
-
alias別名
<alias name="User" alias="OtherUser"/> <!--name需要已經註冊過,getBean()的時候可以傳別名,下面是另一種別名方法--> <bean id="User" class="com.qjl.demo.pojo.User" name="OtherUser"/> <!--name還可以取多個比如,name="OtherUser1,OtherUser2",也可以用空格分割-->
-
scope(bean作用域)
<bean id="User" class="com.qjl.demo.pojo.User" scope="singleton"/> <!--默認是singleton就是單例模式,該bean只會創建一個實例--> <!--採用prototype的話,每次引用都會創建新的實例或者每次getBean()取出來的實例都不一樣-->
-
import多個配置合成一個,當然配置上下文可以直接合併到已有配置文件
<import resource="OtherXml.xml"/> <!--在beans標籤裏面,還有不要重名-->
-
-
其他複雜注入
<!--數組--> <property name="names"> <array> <value>小A</value> <value>小B</value> <value>小C</value> </array> </property> <!--list--> <property name="names"> <list> <value>小A</value> <value>小B</value> <value>小C</value> </list> </property> <!--map--> <property name="names"> <map> <entry key="name" value="QJL"/> <entry key-ref="name" value-ref="QJL(引用型)"/> </map> </property> <!--null--> <property name="names"> <null/> </property> <!--Properties--> <property name="names"> <props> <prop key="A">value1</prop> <prop key="B">value2</prop> <prop key="C">value3</prop> </props> </property>
-
spring知識點詳解
-
bean的自動裝配
spring在上下文中自動尋找,並自動給bean裝配屬性。
分爲三種方式:
-
xml手動裝配(上面已講)
-
java代碼中裝配(註解方式,最常用)
準備工作:使用註解需要以下操作
beans配置約束:
xmlns:context="http://www.springframework.org/schema/context"
beans裏面開啓註解:
<context:annotation-config/>
建議複製以下代碼,因爲還有xsi:schemaLocation需要配置
<?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 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>
註解的使用:
//假設User類依賴Car類,前面加上@Autowired後就自動注入了,前提是 public class User{ //required=false具體功能是xml文件沒有註冊賦值就會爲空,有則賦值。不寫默認是true,這樣的話該屬性不能爲空,xml文件必須註冊,否則報錯。 @Autowired(required = false) @Qualifier("User") //有多個註冊用@Qualifier指定 private Car car; //或者在setter方法上使用,二選一 @Autowired public void setCar(Car car){ this.car = car; } } //這裏還可以用java提供的註解@Resource(name="User")來代替@Autowired
在屬性上使用@Autowired是使用反射實現,不調用setter方法
在setter方法上使用@Autowired直接調用setter方法注入
多個註冊找不到使用@Qualifier指定
-
隱式自動裝配(配置屬性)
<bean id="User" class="com.qjl.demo.pojo.User" autowire="byName"/> <!--這樣會根據User類中的屬性自動在該xml文件尋找相同名稱的已經註冊的類注入,基本數據類型還是自己寫property吧-->
<bean id="User" class="com.qjl.demo.pojo.User" autowire="byType"/> <!--這樣會根據User類中的屬性自動在該xml文件尋找相同類型的已經註冊的類注入。-->
-
到這裏注意:
注意點:byName找的是setter方法的名字,記得setter方法一定要和註冊的bean的id對應(setName(),註冊的bean的id就必須是name),這樣方法名旁邊就會有個綠色的葉子,說明可以成功注入。
注意點:byType要保證註冊的id唯一不然會報錯。
-
-
bean的註冊(註解法)
-
添加約束(上面已講)
-
啓用註解
<!--這裏介紹一種指定包開啓註解的方式,需要配合@Component來使用--> <context:component-scan base-package="com.qjl.pojo"/> <!--在類需要註冊的類前加上@Component,可以省去在xml裏面配置bean的步驟--> <!--當然這句也要加上--> <context:annotation-config/>
@Component //註冊 @Scope("prototype") //默認是”singleton單例模式“ public class User{ @Value("Qjl ") //簡單的用value private String name; }
-
MVC架構的四個同功能註解(不懂請先了解MVC架構):
(dao接口)—>(service)—>(controller)—>(前端)
-
dao層(@Repository)
-
service層(@Service)
-
controller(@Controller)
上面三個註解功能和@Component註解功能一樣都是將類交給spring管理,代替xml中配置bean。
<bean id="User" class="com.qjl.demo.pojo.User"/>
(用註解代替這個語句)。這三個註冊的方法參考@Component,和它一樣就行了。
注意這種方式一定要讓context:component-scan掃到這個包,上面有配置
-
-
-
拓展知識(使用javaconfig代替xml文件管理bean)
//這是spring5之後逐漸開始推薦的一種方法,自行了解即可(beans.xml文件還是很多人用的)。 @Configuration @ComponentScan("com.qjl.demo") //類似掃描包 public class AppConfig{ @Bean public MyService myService{ return new MyServiceImpl(); } }
深入瞭解spring(AOP)
-
代理模式
-
靜態代理(簡單舉例)
public interface AskForMoney{ void argue(); } public class Owner implements AskForMoney{ public void argue(){ System.out.println("討債"); } } public class Company implements AskForMoney{ private Owner owner; public Company(){ owner = new Owner(); } public void argue(){ System.out.println("討債公司上門"); owner.argue(); } } public class Test{ public static void main(String args[]){ Company company = new Company(); company.argue(); } } //這個例子就是使用了代理來執行,討債公司替債主上門討債。
-
動態代理
- 基於接口:JDK動態代理
- 基於類:cglib
- java字節碼實現:javasist(不需要了解虛擬機指令,不像asm)
-
-
JDK動態代理(拓展知識)
-
proxy類
-
InvocationHandler接口
//這裏將一個接口實現類傳給target 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{ insert("AOP"+method.getName()); Object result = method.invoke(target,args); return result; } public void insert(String msg){ System.out.println("這就是"+msg); } } public class Demo{ public static void main(String args){ IUserImpl user = new IUserImpl(); ProxyInvocationHandler pi = new ProxyInvocationHandler(); pi.setTarget(user); IUser iu = (IUser)pi.getProxy(); iu.func(); } } //這裏使用了java的放射實現了AOP,其中IUser是一個接口,IUserImpl實現了接口,通過代理調用接口的func()的同時,前面也調用了insert("AOP")方法,這樣無論調用什麼IUser的什麼方法,都會執行invoke裏面的設置的方法,無形中一次性給IUser所有的方法都插了一段代碼,這就是所謂的AOP,面向切面,類後期被切開,自己放功能進去。
-
-
spring裏的API實現AOP
-
切入點
- 方法前:MethodBeforeAdvice
- return後:AfterReturnAdvice
- 方法前後:MethodInterceptor
- 異常時:ThrowsAdvice
//注意是這個包org.springframework.aop.MethodBeforeAdvice; public class Before implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass(). getName()+"的"+method.getName()+"被執行了"); } } //這就是給方法前面添加Method其他自己類比。
<aop:config> <aop:pointcut id="pointcut" expression= "execution(String com.qjl.demo.pojo.*.*(..)))"/> <aop:advisor advice-ref="after" pointcut-ref="pointcut"/> <aop:advisor advice-ref="before" pointcut-ref="pointcut"/> </aop:config> <!--pointcut指定切入點,after是上面註冊的bean,自己註冊一下,execution表達式下面講解-->
execution語句:
String com.qjl.demo.User.scream(…)
表示爲com.qjl.demo.User下的返回值爲String的scream方法注入環繞。其中…表示參數個數不定,類型不定。
* com.qjl.demo.*.*(String,int)
表示爲com.qjl.demo下的所有參數是(String str,int age)的方法注入環繞,其中*表示通配。
execution(* *(…))
爲項目所有類的所有方法注入環繞
-
-
使用自定義切面
//切面類提供切面方法 public class aspectj { public void before(){ System.out.println("before"); } public void after(){ System.out.println("after"); } }
<!--第5行是註冊的切面類的id,所有要用的都要註冊,這裏不寫了--> <!--第6行指定切入點--> <!--第7、8行指定第5行註冊的切面類的哪個方法切入切入點,切入的位置由aop:before這樣的標籤決定--> <aop:config> <aop:aspect ref="aspectj"> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
-
使用註解實現AOP
-
依賴下載
<!--maven文件依賴--> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
-
AOP配置
<!--頭文件約束引入--> xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation= "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
-
開啓註解配置
<aop:aspectj-autoproxy/> <!--別忘了註冊類,在beans.xml文件配置bean-->
-
標註一個切面
@Aspect public class AnnoAspect{ @Before("execution(* *(..))") public void before(){ System.out.println("before"); } @After("execution(* *(..))") public void after(){ System.out.println("after"); } @Around("execution(* *(..))") //可以只使用上面兩個方法。 public void around(ProceedingJoinPoint point) throws Throwable { System.out.println(point.getSignature()); Object proceed = point.proceed(); System.out.println(point.getSignature()); } } //若三個方法一起使用,則point.proceed()是執行自己的方法的開始,以}大括號爲結尾。 //所以執行順序是,13行->4行—>自己的方法->15行->9行
-
-
聲明式事務(使用AOP實現)
-
ACID原則
原子性、一致性、隔離性、持久性。
-
配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--AOP植入的事務管理,指定事務管理器,指定爲哪種方法開啓事務(示例)--> <tx:advice id="tx" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add"/> <tx:method name="*"/> </tx:attributes> </tx:annotation-driven>
植入事務
<aop:config> <aop:pointcut id="pointCut" expression="execution(* com.qjl.dao.*.*(..))"/> <aop:advisor advice-ref="tx" pointcut-ref="pointCut"/> </aop:config>
-
完結撒花
給學過spring的同學複習用的哈,大家自己統籌兼顧