Spring基础使用

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();
    }
}

执行单元测试,结果如下:

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());
    }

执行结果:

Config{driverClass='com.mysql.jdbc.Driver', jdbcUrl='///spring-learning', username='root', password='ccc', infoMap={a=a, b=b}, properties={p2=c, p1=1}}

Spring可以注入的属性值有:
基础数据类型,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";
    }

执行结果如下:

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 分别表示横切逻辑代码所执行的时机。也即我们希望在目标方法执行的什么时候进行增强。
    从测试结果来分析,这几个切入时机执行顺序分别为:
    before > around前置处理 > 目标方法逻辑(around joinPoint.proceed) > around后置处理>afterReturing / afterThrowing > after
    注:afterReturning与afterThrowing只会执行其中之一,因为目标逻辑代码要么正常执行完成,要么出现异常

Spring基于注解的开发

修改测试代码前两部分如下:

 @Before
    public void init() {
        // applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        applicationContext = new AnnotationConfigApplicationContext(BootConfig.class);
    }

在ServiceA,ServiceB,ServiceC类上分别加上@Service注解,setter方法参数前面加上@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";
    }
}

启动测试方法,执行结果如下:

serviceA doSth
ServiceB doSth

Bean注解

  • @Component 声明当前Class为一个ioc bean,Spring扫包的时候会将其作为一个需要管理的bean来解析并实例化
  • @Service
  • @Controller
  • @Repository
    本质上后面三个注解与@Component一样的效果,只不过为了区分代码层级,提供了后面三个注解。
  • @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注解开发,需要@EnableAspectJAutoProxy开启
用@Aspec生命切面类,@PointCut定义一个切入点,@Before,@After,@AfterThrowing,@After,@Around分别定义切入时机

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容