SpringBoot系列(1)---无配置文件配置基础1

来源:互联网 发布:淘宝美工知乎 编辑:程序博客网 时间:2024/05/16 13:28

今天开始写关于SpringBoot的笔记,当然这个笔记也是主要给我自己看的。如果有其他开发者也在看我写的笔记的话,提醒一下 SpringBoot的笔记是建基于你已经熟悉使用Spring的前提地下去看的,当然我也会尽可能去重温一下Spring的东西。

关于SpringBoot 这个技术其实近段时间随着SpringCloud越来越火(所谓的微服务)SpringBoot也随着火起来了。其实Spring在3.X已经提供了纯annotation的配置,随着servlet3.0 无配置文件变得流行起来。不需要额外的WEB服务,因为WEB容器已经直接嵌入到你的项目当中,启动程序你只需要执行你写好的main方法即可启动web应用,听起来的确是挺让人兴奋的,笔者在上年 年初学习的SpringBoot,最近也准备认真研究一下SpringCloud 所以特意将SpringBoot的笔记写一下,顺便帮自己重温SpringBoot的技术。别废话马上开始····


首先我们老规矩,贴出maven 的依赖:

<dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-context</artifactId>  <version>${spring.version}</version></dependency><dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-aspects</artifactId>  <version>${spring.version}</version></dependency>

注意如果你想加载的快,建议用Spring的repository:

<repositories>  <repository>    <id>io.spring.repo.maven.release</id>    <url>http://repo.spring.io/release/</url>  </repository></repositories>

聪明的你或许已经看见我有写上Aspect的依赖了,所以我们先做一下AOP的测试,关于Spring-aspects的使用,我以前有一篇详细到我自己都不知道原来写得这么详细,给出哆啦B梦的任意门:Spring AOP 之 Aspect


一、AOP AspectJ

然后我们尝试使用Anntation注解织入和execution表达式织入的AOP

1、定义目标 annotation

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Action {    String name();}

2、编写代织入测试的service类

@Servicepublic class DemoAnntationService {    @Action(name = "What the Fuck")    public void add(){        System.out.println("add executed!");    }}
@Service@Scope("prototype")public class DemoMethodService {    public void add(){        System.out.println("DemoMethodService add() executed!");    }}

3、编写织入配置类

@Aspect@Componentpublic class LogAspect {    @Pointcut("@annotation(com.tony.config.Action)")    public void annotationPointCut() {    }        @After("annotationPointCut()")    public void after(JoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        Action action = method.getAnnotation(Action.class);        System.out.println("注解方式拦截:" + action.name());    }    @Before("execution(* com.tony.service.DemoMethodService.add())")    public void before(JoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        System.out.println("execution 拦截方式:" + method.getName());    }}
到目前为止都是我们原来在Spring当中熟悉的东西,然后我们去掉我们应用的配置文件,使用类+annotation的方式代替配置文件的角色,由于我上面写了脏话,所以我的博客低于16岁不许看。

下面是配置:

@Configuration@ComponentScan("com.tony")@EnableAspectJAutoProxypublic class ApplicationConfig {}

1、@Configuration:代表这个是一个配置类

2、@ComponentScan : 这个很熟悉了,就是扫描的类。

3、@EnableAspectJAutoProxy:启动AspectJ 动态代理。

非常简单,也没有什么难度。

运行看看:

public class App {    public static void main( String[] args )    {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);        DemoAnntationService anntationService = context.getBean(DemoAnntationService.class);        DemoMethodService methodService = context.getBean(DemoMethodService.class);        anntationService.add();        methodService.add();        context.close();    }}
运行结果如下:

add executed!注解方式拦截:What the Fuckexecution 拦截方式:addDemoMethodService add() executed!



二、在annotation使用SpringEL

@Configuration@ComponentScan("com.tony")@EnableAspectJAutoProxy//导入配置文件,但是使用这个注解需要依赖PropertySourcesPlaceholderConfigurer@PropertySource("classpath:test.properties")public class ApplicationConfig {    //普通注入字符串    @Value("ABC")    private String normal;    //通过systemProperties的bean获得系统名称    @Value("#{systemProperties['os.name']}")    private String osName;    //获得Spring 容器中其中一个bean的属性    @Value("#{T(java.lang.Math).random() * 100.0}")    private double randomNumber;    @Value("#{demoServiceForEL.username}")    private String serviceUsername;    //获得本地文件的resource对象    @Value("classpath:text.txt")    private Resource testFile;    //获得配置文件中的配置项    @Value("${test.propertyA}")    private String propertyA;    //获得配置文件中的配置项    @Value("${test.propertyB}")    private String propertyB;    //通过URL获得resource对象    @Value("http://www.baidu.com")    private Resource testUrl;    //这里通过environment获得property    @Autowired    private Environment environment;    //注册一个PropertySourcesPlaceholderConfigurer的Bean    @Bean    public static PropertySourcesPlaceholderConfigurer propertyConfigure(){        return new PropertySourcesPlaceholderConfigurer();    }    public void outputResource() throws IOException {        System.out.println(normal);        System.out.println(osName);        System.out.println(randomNumber);        System.out.println(propertyA);        System.out.println(serviceUsername);        System.out.println(IOUtils.toString(testFile.getInputStream()));        System.out.println(IOUtils.toString(testUrl.getInputStream()));        System.out.println(environment.getProperty("test.propertyB"));    }}
可以看到使用无配置文件的方式EL表达式也是如此的强大,接下来我们看看main方法:

public static void main( String[] args ) throws IOException {    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);    ApplicationConfig config = context.getBean(ApplicationConfig.class);    config.outputResource();    context.close();}
对没错,ApplicationConfig配置类本身也将会成为一个实体bean。

调用之后输出如下结果:

ABCMac OS X57.69163813970949valueATONYABCDEFGURL调用忽略太长了.....valueB


需要注意的是,如果我们读取文件的时候出现异常,需要添加如下的依赖支持:

<dependency>  <groupId>commons-io</groupId>  <artifactId>commons-io</artifactId>  <version>2.5</version></dependency>

三、使用配置类方式配置bean

刚刚上面已经看见了,除了常规的annotation配置bean的方式,也可以使用以下这种方式:

public class BeanWayService {        public void init(){        System.out.println("BeanWayService-init");    }        public BeanWayService(){        System.out.println("BeanWayService-constructor");    }        public void destroy(){        System.out.println("BeanWayService-destroy");    }    }
没有配置任何的@Service @Repository 等标签

@Configuration@ComponentScan("com.tony")@EnableAspectJAutoProxypublic class ApplicationConfig {    @Bean(initMethod = "init",destroyMethod = "destroy")    public BeanWayService beanWayService(){        return new BeanWayService();    }}
上面应该都懂了,先初始化的时候调用init方法,在销毁的时候调用destroy方法。注意:虽然我们没有在代码上显性定义为单例,但是事实上是单例:

public static void main( String[] args ) throws IOException {    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);    BeanWayService wayServiceA = (BeanWayService) context.getBean("beanWayService");    BeanWayService wayServiceB = (BeanWayService) context.getBean("beanWayService");    System.out.println(wayServiceA == wayServiceB);    context.close();}
运行输出如下:

BeanWayService-constructorBeanWayService-inittrueBeanWayService-destroy


当然我们也可以定义他的scope,下面是两个定义scope的方法:

@Configuration@ComponentScan("com.tony")@EnableAspectJAutoProxypublic class ApplicationConfig {    @Bean(initMethod = "init",destroyMethod = "destroy")    @Scope("prototype")    public BeanWayService wayService(){        return new BeanWayService();    }}
Main方法:

public static void main( String[] args ) throws IOException {    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);    BeanWayService wayServiceA = (BeanWayService) context.getBean("wayService");    BeanWayService wayServiceB = (BeanWayService) context.getBean("wayService");    System.out.println(wayServiceA == wayServiceB);    context.close();}
可能你已经发现,我定义bean的方法的方法名改了,相对应的我在main方法当中的bean名称也发生了改变,由此得出定义bean的方法名其实就是定义bean的id

以下是运行结果:

BeanWayService-constructorBeanWayService-initBeanWayService-constructorBeanWayService-initfalse

如果是使用annotation定义bean的话,我们只需要在类中打上@Scope标签即可:

@Service@Scope("prototype")public class DemoServiceForEL {    public String username = "TONY";}


四、使用Profile

profile的主要作用是,当我们需要在不同环境当中获得不同的配置,或者不同的环境中获得不同的bean,这个时候就可以使用Profile。

设置当前的Profile有如下3种方式:

1、通过Environment的ActiveProfiles 设置当前的context的环境。

2、通过JVM的spring.profiles.active参数来进行指定context的环境。

3、WEB项目通过Servlet中的context parameter进行设置

3.1 servlet2.5以下:在DispatcherServlet 中的init-param标签,添加spring.profiles.active的配置。

3.2 servlet3.0以上:在ServletContext 对象中setInitParameter("spring.profiles.default","当前环境字符串") 进行配置。


由于我当前不是web项目,所以我使用第一种方式进行演示:

定义个demoBean类:

public class DemoBean {    private String content;    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;    }    public DemoBean(String content) {        this.content = content;    }}
配置类如下:

@Configuration@ComponentScan("com.tony")@EnableAspectJAutoProxypublic class ApplicationConfig {    @Profile("dev")    @Bean("demoBean")    public DemoBean devDemoBean(){        return new DemoBean("DEV");    }    @Profile("prod")    @Bean("demoBean")    public DemoBean prodDemoBean(){        return new DemoBean("PROD");    }}
可以看见我使用了@Profile指定了不同环境的名称,由于我们不可能写两个相同方法名称的方法,所以我们使用了@Bean中的name属性去定义bean的名称。

main方法:

public class App {    public static void main( String[] args ) throws IOException {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();        context.getEnvironment().setActiveProfiles("dev");        context.register(ApplicationConfig.class);        context.refresh(); //记得刷新一下配置        DemoBean demoBean = (DemoBean) context.getBean("demoBean");        System.out.println(demoBean.getContent());    }}
我们一开始在AnntationConfigApplicationContext的构成中并没有去定义具体的配置类,获得contex对象之后更改profile 然后注册ApplicationConfig配置类,然后刷新配置。以下是运行结果:

DEV
如果换环境只需要我们将setActiveProfiles修改为prod即可。


五、事件

如果有android开发经验的同学应该已经有用过EventBus了,而Spring中的事件与ANDROID中的EventBus非常相似。Spring中的事件主要提供多个bean进行某个Event的事件监听。以下就开始上代码:

1、我们需要继承ApplicationEvent定义自定义事件

public class DemoEvent extends ApplicationEvent {    private String msg;    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public DemoEvent(Object source,String msg) {        super(source);        this.msg = msg;    }}

2、创建需要监听事件的类,需要实现ApplicationListener接口

@Componentpublic class DemoListener implements ApplicationListener<DemoEvent> {    public void onApplicationEvent(DemoEvent demoEvent) {        String msg = demoEvent.getMsg();        System.out.println("got a message from <"+demoEvent.getSource().getClass().toString()+"> :" + msg);    }}
3、创建发送事件的类

@Componentpublic class DemoPublisher {    @Autowired    ApplicationContext applicationContext;    public void publish(String msg){        applicationContext.publishEvent(new DemoEvent(this,msg));    }}
4、main方法调用并输出如下结果:

public class App {    public static void main( String[] args ) throws IOException {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);        DemoPublisher publisher = context.getBean(DemoPublisher.class);        publisher.publish("ABC");    }} 

输出:got a message from <class com.tony.event.DemoPublisher> :ABC

注意:事实上我们监听事件可以不止一个监听事件的bean,所有继承并监听DemoEvent的类对象都会获得事件消息。


原创粉丝点击