Spring基礎使用
Spring IOC
- 新建工程
新建maven工程,然后在pom.xml中添加如下依賴:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
- 測試IOCbean注入
新建三個服務類:ServiceA, ServiceB, ServiceC
package com.test.service;
/**
* @author felix
*/
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void doSth(){
System.out.println("serviceA doSth");
}
}
package com.test.service;
/**
* @author felix
*/
public class ServiceB {
private ServiceC serviceC;
public void setServiceC(ServiceC serviceC) {
this.serviceC = serviceC;
}
public void doSth() {
System.out.println("ServiceB doSth");
}
}
package com.test.service;
/**
* @author felix
*/
public class ServiceC {
private ServiceA serviceA;
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doSth() {
System.out.println("ServiceC doSth");
}
}
新增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 name="serviceA" class="com.test.service.ServiceA">
<property name="serviceB" ref="serviceB"/>
</bean>
<bean name="serviceB" class="com.test.service.ServiceB">
<property name="serviceC" ref="serviceC"/>
</bean>
<bean name="serviceC" class="com.test.service.ServiceC">
<property name="serviceA" ref="serviceA"/>
</bean>
</beans>
注意:bean的依賴項配置在property標簽中,同時需要再依賴方定義成員變量镣衡,并寫好setter方法侨赡,如果不生成setter暴匠,Spring默認是無法注入進來的
新增測試類:
package com.test;
import com.test.service.ServiceA;
import com.test.service.ServiceB;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringFrameworkTests {
private ApplicationContext applicationContext;
@Before
public void init() {
applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void testIoc() {
ServiceA serviceA = (ServiceA) applicationContext.getBean("serviceA");
serviceA.doSth();
ServiceB serviceB = applicationContext.getBean(ServiceB.class);
serviceB.doSth();
}
}
執(zhí)行單元測試涩蜘,結果如下:
serviceA doSth
ServiceB doSth
- 屬性注入
在beans.xml中新增如下配置:
<context:property-placeholder location="db.properties"/>
<bean name="config" class="com.test.service.Config">
<property name="driverClass" value="${db.driverClass}"/>
<property name="jdbcUrl" value="${db.jdbcUrl}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="ccc"/>
<property name="infoMap" ref="infoMap"/>
<property name="properties" ref="properties"/>
</bean>
<util:map id="infoMap">
<entry key="a" value="a"/>
<entry key="b" value="b"/>
</util:map>
<util:properties id="properties">
<prop key="p1">1</prop>
<prop key="p2">c</prop>
</util:properties>
新增db.propertis
db.driverClass=com.mysql.jdbc.Driver
db.jdbcUrl=///spring-learning
db.username=root
db.password=root
新增Config.java
package com.test.service;
import java.util.Map;
import java.util.Properties;
public class Config {
private String driverClass;
private String jdbcUrl;
private String username;
private String password;
private Map<String, Object> infoMap;
private Properties properties;
@Override
public String toString() {
return "Config{" + "driverClass='" + driverClass + '\'' + ", jdbcUrl='" + jdbcUrl + '\''
+ ", username='" + username + '\'' + ", password='" + password + '\'' + ", infoMap="
+ infoMap + ", properties=" + properties + '}';
}
public String getDriverClass() {
return driverClass;
}
public void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Map<String, Object> getInfoMap() {
return infoMap;
}
public void setInfoMap(Map<String, Object> infoMap) {
this.infoMap = infoMap;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
新增測試方法:
@Test
public void testIocProperties() {
Config config = applicationContext.getBean(Config.class);
System.out.println(config.toString());
}
執(zhí)行結果:
Config{driverClass='com.mysql.jdbc.Driver', jdbcUrl='///spring-learning', username='root', password='ccc', infoMap={a=a, b=b}, properties={p2=c, p1=1}}
Spring可以注入的屬性值有:
基礎數(shù)據(jù)類型,bean對象占贫,Properties, Map, Set等,其中Properties,Map,Set我們需要用對應的標簽單獨配置,property 的value部分填入對應的id即可肢簿。
測試AOP
在beans.xml中新增如下配置:
<bean id="logUtil" class="com.test.aop.LogUtil"/>
<aop:config>
<aop:aspect id="logAdvice" ref="logUtil">
<aop:pointcut id="doSthPointCut"
expression="execution(public * com.test.service.ServiceA.doSth())"/>
<aop:before method="printBefore"
pointcut-ref="doSthPointCut"/>
<aop:after method="printAfter"
pointcut-ref="doSthPointCut"/>
<aop:after-returning method="printAfterReturning"
pointcut-ref="doSthPointCut"/>
<aop:after-throwing method="afterThrowing"
pointcut-ref="doSthPointCut"/>
<aop:around method="around" pointcut-ref="doSthPointCut"/>
</aop:aspect>
</aop:config>
新增LogUtil.java
package com.test.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.Pointcut;
public class LogUtil {
/**
* 前面打印
*/
public void printBefore() {
System.out.println("print info from LogUtil.printBefore");
}
/**
* 后面打印
*/
public void printAfter() {
System.out.println("print info from LogUtil.printAfter");
}
/**
* 后面打印
*/
public void printAfterReturning() {
System.out.println("print info from LogUtil.printAfterReturning");
}
/**
* 后面打印
*/
public void afterThrowing() {
System.out.println("print info from LogUtil.afterThrowing");
}
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---around before----");
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("---around after----");
}
}
改造serviceA的doSth方法:
public String doSth(){
System.out.println("serviceA doSth");
// throw new RuntimeException("an exception ");
return "hello";
}
啟動第一步中測試IOC BEAN的測試方法
@Test
public void testSimpleIoc() {
ServiceA serviceA = (ServiceA) applicationContext.getBean("serviceA");
serviceA.doSth();
ServiceB serviceB = applicationContext.getBean(ServiceB.class);
serviceB.doSth();
}
輸出結果如下:
print info from LogUtil.printBefore
---around before----
serviceA doSth
hello
---around after----
print info from LogUtil.printAfterReturning
print info from LogUtil.printAfter
ServiceB doSth
在ServiceA.doSth中拋出異常
public String doSth(){
System.out.println("serviceA doSth");
throw new RuntimeException("an exception ");
// return "hello";
}
執(zhí)行結果如下:
print info from LogUtil.printBefore
---around before----
serviceA doSth
print info from LogUtil.afterThrowing
print info from LogUtil.printAfter
java.lang.RuntimeException: an exception
一堆異常信息靶剑。。池充。
- aspec指定切面邏輯類桩引,也即是使用被用來處理切面的邏輯達到增強其他方法的目的那個類
- pointcut指定切入點是什么,往往是某個類或者某些類的某些方法收夸。也即是配置我們哪些類的哪些方法需要被增強坑匠。
- before, after, afterReturning, afterThrowing, around 分別表示橫切邏輯代碼所執(zhí)行的時機。也即我們希望在目標方法執(zhí)行的什么時候進行增強卧惜。
從測試結果來分析厘灼,這幾個切入時機執(zhí)行順序分別為:
before > around前置處理 > 目標方法邏輯(around joinPoint.proceed) > around后置處理>afterReturing / afterThrowing > after
注:afterReturning與afterThrowing只會執(zhí)行其中之一夹纫,因為目標邏輯代碼要么正常執(zhí)行完成,要么出現(xiàn)異常
Spring基于注解的開發(fā)
修改測試代碼前兩部分如下:
@Before
public void init() {
// applicationContext = new ClassPathXmlApplicationContext("beans.xml");
applicationContext = new AnnotationConfigApplicationContext(BootConfig.class);
}
在ServiceA,ServiceB,ServiceC類上分別加上@Service注解设凹,setter方法參數(shù)前面加上@Autowired注解舰讹,ServiceA如下:
@Service
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(@Autowired ServiceB serviceB) {
this.serviceB = serviceB;
}
public String doSth(){
System.out.println("serviceA doSth");
// throw new RuntimeException("an exception ");
return "hello";
}
}
啟動測試方法,執(zhí)行結果如下:
serviceA doSth
ServiceB doSth
Bean注解
- @Component 聲明當前Class為一個ioc bean,Spring掃包的時候會將其作為一個需要管理的bean來解析并實例化
- @Service
- @Controller
- @Repository
本質上后面三個注解與@Component一樣的效果闪朱,只不過為了區(qū)分代碼層級月匣,提供了后面三個注解。 - @Autowired 表明某個屬性需要ioc容器來注入奋姿,spring通過類型來找到需要注入的bean, 同時可以通過@Qualifier("beanName")來通過名稱注入锄开。
用AOP注解改造LogUtil.java
package com.test.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
@Component
public class LogUtil {
@Pointcut("execution(public * com.test.service.ServiceA.doSth())")
public void pointCut() {
}
/**
* 前面打印
*/
@Before("com.test.aop.LogUtil.pointCut()")
public void printBefore() {
System.out.println("print info from LogUtil.printBefore");
}
/**
* 后面打印
*/
@After("com.test.aop.LogUtil.pointCut()")
public void printAfter() {
System.out.println("print info from LogUtil.printAfter");
}
/**
* 后面打印
*/
@After("com.test.aop.LogUtil.pointCut()")
public void printAfterReturning() {
System.out.println("print info from LogUtil.printAfterReturning");
}
/**
* 后面打印
*/
@AfterThrowing("com.test.aop.LogUtil.pointCut()")
public void afterThrowing() {
System.out.println("print info from LogUtil.afterThrowing");
}
@Around("com.test.aop.LogUtil.pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---around before----");
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("---around after----");
return result;
}
}
改造SpringConfig.java
/**
* @author felix
*/
@Configuration
@ComponentScan(basePackages = "com.test")
@EnableAspectJAutoProxy
public class BootConfig {
}
啟動測試,結果如下:
---around before----
print info from LogUtil.printBefore
serviceA doSth
print info from LogUtil.printAfterReturning
print info from LogUtil.printAfter
hello
---around after----
ServiceB doSth
AOP注解開發(fā)称诗,需要@EnableAspectJAutoProxy開啟
用@Aspec生命切面類萍悴,@PointCut定義一個切入點,@Before,@After,@AfterThrowing,@After,@Around分別定義切入時機