spring

  说到spring,那么什么是spring?一句话概括,就是一个Java的一个开源框架。目的是:简化Java开发。核心是DI和AOP(依赖注入、面向切面编程)。在spring中,用bean 来表示应用组件。

  什么是DI(依赖注入)?:在传统的Java方法。A类的A1方法调用B类的B1方法,那个B对应的Java类需要实例化变成一个对象来调用(这里排除static声明的类)。但是有了DI之后,不用实例化了,spring容器自动给你实例化了。你需要注入该对象,然后直接使用。

  spring的DI(依赖注入)简单理解了,那么你一定很好奇,什么东西可以管理这些bean ,以至于你都不用实例化对象?答案就是spring容器。当然spring有很多容器,他的作用就是管理这些个时而被需要,时而被抛弃的bean。现在基本上会用应用上下文容器比较多一些,bean工厂会少一些,因为他比较简单。具体容器是怎么做到的,这里不展开,因为spring已经封好了,我们就直接用吧。

  还有一点你可能会有疑问,容器只是负责管理这些bean,那么什么样的对象可以被创建为bean。如果没有这些对象那么就无法创建bean。他也没有办法去管理啊。在spring中,用bean 来表示应用组件,那么引出配置一个组件。这里大致可以分为3种方式来配置bean。自动化装配bean,通过Java代码来配置bean。通过配置XML方式来配置bean。当然也可以混合配置。

  • 自动换装配bean。这种方式真棒,自动化,自然需要配置的东西就越来越少。怎么配置呢?这里引用《spring实战》的例子。  
package soundsystem;

public interface CompactDisc {

    void play();
}
import org.springframework.stereotype.Component;

/**
 *@Component:
 *1.表明该类会作为组件类,
 *2.并告诉spring要为这个类创建bean,这个bean的ID默认是类名(该类名的第一个字母小写),若改名,则@Component("xxx")或者
 *    @Named("xxxx")
 *
 *
 */
@Component
public class SgtPeppers implements CompactDisc {
    
    private String title = "Sgt.Pepper's Lonely Hearts Club Band";
    private String artist = "The Beatles";
    @Override
    public void play() {
        System.out.println("Playing "+title+" by "+artist);
    }

}
package soundsystem;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @ComponentScan:
 * 1.默认扫描和配置类相同的包以及该包的子包
 * 2.指定扫描的包@ComponentScan("soundsystem");
 * 3.@ComponentScan(basePackages="soundsystem");
 * 4.@ComponentScan(basePackages={"soundsystem","soundsystem2"});
 * 注意:2,3,4中的方法设置基础包是String类型的,安全性角度来说是不安全的,建议是采用组件的方式;
 * @ComponentScan(basePackagesClasses={soundsystem.calss,soundsystem2.class});
 */
@Configuration//通过Java代码定义了spring的装配规则,
@ComponentScan//在spring中启动组件扫描
public class CDPlayerConfig {

}

  这里,用到了@Component注解,@Configuration注解,@ComponentScance注解。那么具体让我们看看这个DI到底是怎么做到不用实例化对象的呢。

  代码介绍:SgtPeppers 类实现CompactDisc接口,来打印一条信息。就是简单一个需求。那么需要怎么操作呢?看下面的JUnit测试方法。

package soundsystem;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
    
    /**
     * @Autowired:声明自动装配,让spring自动满足bean依赖的一种方法
     *             换句话来说,就是通过@Autowired来实例化并且会传入一个一可设置的bean 
     *             为了避免没有找到该bean,一般写明是否必要
     *  @Autowired(required=false),spring在实现自动装配的时候,若没有匹配的bean,spring会让这个bean处于未匹配状态
     *  若有多个bean可匹配的请求,spring会抛出异常
     *  @Autowired 同@Inject
     */
    @Autowired
    private CompactDisc cd;
    
    @Autowired
    private CompactDisc player;
    
//    @Test
//    public void cdShouldNotBeNull(){
//        assertNotNull(cd);
//    }
    
    @Test
    public void play(){
        player.play();
    }

}
@Autowired这里又用到了这个注解。看到JUnit测试类,这里我们没有实例化对象。这个play方法可以测试通过。会打印出来那么一句话。。。。(你懂的)。具体他是怎么做到的?
@Component:带该注解,那么就是告诉容器,我这个类需要bean。那么容器就给他一个首字母小写的一个bean。这里就是sgtPeppers。那么容器去哪里找这个bean呢?看到@ComponentScan,他去扫描组件。去哪里扫描呢,默认是本包(包括子包)。
@ComponentScan扫描本包(包括子包)找到带@Component的类,然后来创建bean。有了bean,当需要这个bean的时候,容器自动创建他,不需要时则被回收。这里需要调用play方法,使用@Autowired方法自动装配完成,直接调用,无需实例化对象。
@ComponentScan使用的前提是,必须要有@Configuration。
  • 第二种,java代码来配置bean。

  Java代码,感觉应该也还好,至少会在配置第三方组件的时候会用。这里我去掉了@ComponentScan注解,用代码来创建了Bean ,而不是通过@Component组件了。

package soundsystem2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//通过Java代码定义了spring的装配规则,
public class CDPlayerConfig {

    /**
     * 声明简单的Bean
     *@Bean:告诉spring这个方法返回一个对象,该对象要注册为spring应用上下文中的bean。bean默认ID是方法名即 sgtPeppers
     *        更改bean id @Bean(name="xxxx")
     */
    @Bean
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }

    
}

  这里用到@Bean注解。JUnit测试结果是一样的。就是这么简单。

  • 第三种,xml方式配置bean。当然需要先创建一个Spring Bean Definition file(spring的配置文件)。这里需要bean 类是soundsystem2.SgtPeppers,所以把他配置bea。
<?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="sgtPeppers" class="soundsystem2.SgtPeppers"></bean>
</beans>

JUnit测试通过。

package soundsystem2;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes=CDPlayerConfig.class)
@ContextConfiguration(locations="classpath:soundsystem2/springxml.xml")
public class CDPlayerTest {
    
    /**
     * @Autowired:声明自动装配,让spring自动满足bean依赖的一种方法
     *             换句话来说,就是通过@Autowired来实例化并且会传入一个一可设置的bean 
     *             为了避免没有找到该bean,一般写明是否必要
     *  @Autowired(required=false),spring在实现自动装配的时候,若没有匹配的bean,spring会让这个bean处于未匹配状态
     *  若有多个bean可匹配的请求,spring会抛出异常
     *  @Autowired 同@Inject
     */
//    @Autowired
//    private CompactDisc cd;
    
    @Autowired
    private SgtPeppers player;
    
//    @Autowired
//    private CDPlayer cdPlayer;
    
//    @Test
//    public void cdShouldNotBeNull(){
//        assertNotNull(cd);
//    }
    
    @Test
    public void play(){
        player.play();
    }
    


}

就是这么简单。当应该程序比较复杂的时候,可想而知,通过这种方式配置bean可能会有点小崩溃。

  下面理解一下,spring中的aop。什么是AOP(面前切面编程)?其实就是AOP作用就是:把分散在各个地方的功能分离出来形成组件。(这里总是出现组件这个词,那么组件是什么?简单理解他就是一个Java类。)

  spring侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决在企业级开发中的常见问题。spring具体实现AOP原理这里不展开。从spring层面来应用AOP。通过我们还是讲注解方式。具体应用,想看下面的代码。

  

package concert;

public interface Performance {
    
    public void perform();
    
}
package concert;

import org.springframework.stereotype.Component;

@Component
public class Performance2 implements Performance {

    @Override
    public void perform() {
        System.out.println("joy ");
    }

}

需求很简单,就是去看一个演出。perform()方法就是一个演出。那么在演出之前。我们也要去找座位。找完座位后,关闭手机等待演出开始。然后再是开始演出。如果演出没有意外,那么就鼓掌。否则退票。演出方法就有了。也就是 先执行“找座位方法 关闭手机等待演出” ;接下来执行perform()方法;然后执行 “鼓掌” or "退票方法";

那么我们可以通过AOP这么做:(这里需要两个第三方jar:aopalliance-.jar和 aspectj-weaver.jar)。这里需要解释一下SpringAop与AspectJ(一个基于Java语言的AOP框架)的联系与区别。这里我们所讲的是Spring集成AspectJ的注解模式。

1.@Aspect表示一个切面。先创建一个切面。

2.创建切点@Pointcut("execution(** concert.Performance.perform(..))")设置 concert.Performance.perform方法(任意参数的该方法)为切点。

3.创建通知。@Around("performance()") performance()指向的是设置切点的方法。当执行测试类的时候控制台打印3行文字,也就实现了我们这个需求。具体是什么,请你试一下。

注:通知可以分为很多种。切点的设置也有很多种,这里为了单纯的理解AOP,不做展开。附上spring 的文档地址,供读者查看(https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/htmlsingle/)。

 附上:

package concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Audience {

//    
    //设置切点
    @Pointcut("execution(** concert.Performance.perform(..))")
    public void performance(){
        
    }

    /**
     *设置环绕通知 
     */
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        System.out.println("Silencing cell phones");
        try {
            jp.proceed();
            System.out.println("clap");
        } catch (Throwable e) {
            System.out.println("Demanding a refund");
        }
        
    }
    
    
}

 

配置文件:

package concert;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {


}

 如果你选择 xml 配置方式 一下配置供参考 :

<aop:aspectj-autoproxy proxy-target-class="true" />
    <bean id="audience" class="concert.Audience"></bean>

 

测试类:

package concert;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ConcertConfig.class)
public class Test {

    @Autowired
    private Performance performance;
    @org.junit.Test
    public void test() {
        performance.perform();

        
    }

}

简单实现功能后,你会不会有这么疑问?既然配置都是通过方法来配置,是不是可以传递参数。当然是可以的。

package concert;

public interface Performance {
    
    public void perform();
    
    public void perform(int a);
}
package concert;

import org.springframework.stereotype.Component;

@Component
public class Performance2 implements Performance {

    @Override
    public void perform() {
        System.out.println("joy ");
    }

    @Override
    public void perform(int a) {
        System.out.println("joy  2....");
    }
}
//设置切点
        @Pointcut("execution(** concert.Performance.perform(..))")
        public void performance(){
            
        }

    /**
     *设置环绕通知 
     *效果同上是一样的
     */
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        System.out.println("Silencing cell phones");
        try {
            jp.proceed();
            System.out.println("clap");
        } catch (Throwable e) {
            System.out.println("Demanding a refund");
        }
        
    }
    
    /**
     *设置切点
     * concert.Performance.perform(int),该方法的int参数也会传递到通知中去
     *  args(a):指定参数a
     */
    @Pointcut("execution(* perform*(..))&& args(a)")
        public void performance2(int a){
            
        }
        

        /**
         *设置环绕通知 
         *效果同上是一样的
         */
        @Around("performance2(a)")
        public void watchPerformance2(ProceedingJoinPoint jp,int a){
            System.out.println("Silencing cell phones and param:"+a);
            try {
                jp.proceed();
                System.out.println("clap and param:"+a);
            } catch (Throwable e) {
                System.out.println("Demanding a refund");
            }
            
        }
package concert;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ConcertConfig.class)
public class Test {

    @Autowired
    private Performance performance;
    
    @org.junit.Test
    public void test() {
        performance.perform();
        performance.perform(2);

    }

}

测试结果中应该是带有参数的。

AOP还可以通过注解来引入新功能。比如我想引入这个功能:

package concert;

import org.springframework.stereotype.Component;

@Component
public class OtherFunctionImpl implements OtherFunction{

    @Override
    public void eat() {
        System.out.println("eat");
    }

}

在切面中增加该功能。

package concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Audience {

    //设置切点
        @Pointcut("execution(** concert.Performance.perform(..))")
        public void performance(){
            
        }

    /**
     *设置环绕通知 
     *效果同上是一样的
     */
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        System.out.println("Silencing cell phones");
        try {
            jp.proceed();
            System.out.println("clap");
        } catch (Throwable e) {
            System.out.println("Demanding a refund");
        }
        
    }
    
    /**
     *设置切点
     * concert.Performance.perform(int),该方法的int参数也会传递到通知中去
     *  args(a):指定参数a
     */
    @Pointcut("execution(* perform*(..))&& args(a)")
        public void performance2(int a){
            
        }
        

        /**
         *设置环绕通知 
         *效果同上是一样的
         */
        @Around("performance2(a)")
        public void watchPerformance2(ProceedingJoinPoint jp,int a){
            System.out.println("Silencing cell phones and param:"+a);
            try {
                jp.proceed();
                System.out.println("clap and param:"+a);
            } catch (Throwable e) {
                System.out.println("Demanding a refund");
            }
            
        }
        
        /**
         * @DeclareParents 注解由3部分构成
         * 1.value:指向了哪种类型的bean要引入该接,这里就是performance 接口(及其子接口)
         * 2.defaultImpl 引入实现类。
         * 3.属性部分,引入接口
         */
        @DeclareParents(value="concert.Performance+",defaultImpl=OtherFunctionImpl.class)
        public static OtherFunction otherFunction;
        
        
    
    
}

测试类:

package concert;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ConcertConfig.class)
public class Test {

    @Autowired
    private Performance performance;
    
    @org.junit.Test
    public void test() {
        performance.perform();
        performance.perform(2);
        
         OtherFunction e = (OtherFunction) performance;
         e.eat();;
        
    }

}

 

 springmvc 运行流程:

 

 

  

  

 

 

 




posted @ 2018-06-10 15:54  啄木鸟伍迪  阅读(246)  评论(0)    收藏  举报
//火箭 GenerateContentList();