zoukankan      html  css  js  c++  java
  • Spring的常用注解

    Spring框架主要包括IoC和AOP,这两大功能都可以使用注解进行配置。

    开发环境:IntelliJ IDEA 2019.2.2
    Spring Boot版本:2.1.8
    新建一个名称为demo的Spring Boot项目。

    一、bean定义

    在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。
    bean是一个由Spring IoC容器实例化、组装和管理的对象。
    使用@Component、@Service或@Configuration注解来修饰一个类,这些类会被Spring自动检测并注册到容器中,在类里面使用@Bean注解修
    饰的方法,会被视为一个bean存放到Spring容器中。

    下面例子实现了怎样根据类型获取bean的名称,获取bean;

    1、新建类 MyBean.java

    package com.example.demo;
    
    public class MyBean {
    
        public MyBean(String id){
           this.id = id;
        }
    
        private String id;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    }

    2、新建类 MyConfig.java

    package com.example.demo;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MyConfig {
    
        //默认bean的名称为方法名,即下面的getMyBean
        @Bean
        public MyBean getMyBean(){
            return new MyBean("1");
        }
    
        //设置bean的名称为bean
        @Bean("bean2")
        public MyBean getMyBean2(){
            return new MyBean("2");
        }
    }

    3、修改启动类代码 DemoApplication.java

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication  {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        @Autowired
        ApplicationContext ctx;
    
        @RequestMapping(value = "/")
        public String index(){
            //根据类型获取bean的名称
            String[] names = ctx.getBeanNamesForType(MyBean.class);
            for(String name : names){
                System.out.println(name);
            }
    
            //获取bean
            MyBean bean1 = (MyBean)ctx.getBean("getMyBean");
            System.out.println(bean1.getId());
    
            MyBean bean2 = (MyBean)ctx.getBean("bean2");
            System.out.println(bean2.getId());
    
            return "";
        }
    }

    运行项目后,浏览器访问:http://localhost:8080/,IDEA控制台输出:
    getMyBean
    bean2
    1
    2

    项目结构

     二、依赖注入

    使用注解可以实现实例的注入,最常用的是@Resource及@Autowired。
    @Resource是JSR-250定义的注解,默认会根据名称进行注入。
    @Autowired默认会根据类型进行注入。

    1、继续使用上面例子的两个类 MyBean.java、MyConfig.java

    2、修改启动类代码 DemoApplication.java

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication  {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        //使用@Resource注入
        @Resource(name="getMyBean")
        MyBean myBean1;
    
        //使用@Autowired注入
        @Autowired
        MyBean bean2;
    
        @RequestMapping(value = "/")
        public String index(){
            System.out.println(myBean1.getId());
            System.out.println(bean2.getId());
            return "";
        }
    }

    浏览器访问:http://localhost:8080/,IDEA控制台输出:
    getMyBean
    bean2
    1
    2

    备注:
    上面MyBean bean2的bean2不能修改为别的名称。原因:
    @Autowired根据类型进行注入,如果容器中只有一个MyBean类型的bean,则bean2可以随便命名。
    但本例子容器中有两个MyBean类型的bean,碰到这种有多个bean的情况,则根据属性名来查找,这里属性名bean2最终会找到相应的bean。
    如果把MyBean bean2改成MyBean myBean2,则运行时IEAD控制台会报异常信息:

    Field myBean2 in com.example.demo.DemoApplication required a single bean, but 2 were found:
    	- getMyBean: defined by method 'getMyBean' in class path resource [com/example/demo/MyConfig.class]
    	- bean2: defined by method 'getMyBean2' in class path resource [com/example/demo/MyConfig.class]

    以上例子的注入方式为设值注入,还可以使用构造注入,向控制器的构造器中注入bean。
    修饰构造器只能使用@Autowired注解,@Resource不能修改构造器。
    例子:

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication  {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        MyBean bean2;
    
        //构造注入
        @Autowired
        public DemoApplication(MyBean bean2){
            this.bean2 = bean2;
        }
    
        @RequestMapping(value = "/")
        public String index(){
            System.out.println(bean2.getId());
            return "";
        }
    }

    三、使用Primary注解

    根据类型来注入,如果容器中存在多个相同类型的bean时,会抛异常,因为此时Spring不知道将哪个bean注入。
    针对这个问题,可以使用@Primary注解。

    1、修改MyConfig.java代码,为第一个bean增加注解@Primary

    package com.example.demo;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    @Configuration
    public class MyConfig {
    
        //默认bean的名称为方法名,即下面的getMyBean
        @Bean
        @Primary
        public MyBean getMyBean(){
            return new MyBean("1");
        }
    
        //设置bean的名称为bean
        @Bean("bean2")
        public MyBean getMyBean2(){
            return new MyBean("2");
        }
    }

    2、启动类代码 DemoApplication.java还是用上面例子

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication  {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        //使用@Resource注入
        @Resource(name="getMyBean")
        MyBean myBean1;
    
        //使用@Autowired注入
        @Autowired
        MyBean bean2;
    
        @RequestMapping(value = "/")
        public String index(){
            System.out.println(myBean1.getId());
            System.out.println(bean2.getId());
            return "";
        }
    }

    浏览器访问:http://localhost:8080/,IDEA控制台输出:
    1
    1

    四、Scope注解

    配置bean时可以指定bean的作用域(scope),一般的bean可以配置为单态(singleton)或者非单态(prototype)。
    配置为singleton,Spring的bean工厂只返回同一个bean的实例。
    配置为prototype,则每次会创建一个新的实例。

    1、修改代码 MyConfig.java

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication  {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        @Autowired
        ApplicationContext ctx;
    
        @RequestMapping(value = "/")
        public String index(){
            String s = "prototype:" + ctx.getBean("bean1") + "<br /> "
                      + "singleton:" + ctx.getBean("bean2") + "<br /> ";
            return s;
        }
    }

    浏览器访问:http://localhost:8080/,多次刷新,页面内容都变化:
    prototype:com.example.demo.MyBean@6fce7ec4
    singleton:com.example.demo.MyBean@7626c5f9
    prototype:com.example.demo.MyBean@357b01f6
    ......

    注意:
    如果在一个单态的bean里面注入一个非单态的bean,则这个单态的bean所维护的非单态bean实例,将不会被刷新。
    例子Spring MVC的控制器是单态的,如果往控制器里面注入一个非单态的bean,如下所示:

        //注入一个非单态的bean
        @Autowired
        private MyBean bean1;
    
        @RequestMapping(value = "/")
        public String index(){        
            return bean1.toString();
        }

    浏览器访问:http://localhost:8080/,多次刷新,页面都是显示如下:
    com.example.demo.MyBean@5d0c61c9
    说明index()方法输出的MyBean都是调用同一个实例,因为控制器在初始化时,就已经被注入了一个bean,而且一直维护着同一个实例。

    五、方法注入

    如果在一个单态的bean里面注入一个非单态的bean,则这个单态的bean所维护的非单态bean实例,将不会被刷新。
    有两种简单的解决方法:
    1、在需要注入的一方(单态的bean),直接使用ApplicationContext,每次调用非单态的bean,都由容器返回。

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class DemoController {
    
        @Autowired
        private ApplicationContext ctx;
    
        private MyBean getBean1(){
            return (MyBean)ctx.getBean("bean1");//一个非单态的bean
        }
    
        @RequestMapping(value = "/")
        public String index(){
            return getBean1().toString();
        }
    }

    浏览器访问:http://localhost:8080/,多次刷新,页面每次都变化:
    com.example.demo.MyBean@12a7cacc
    com.example.demo.MyBean@1776b0ea
    ......


    2、使用Spring的方法注入。
    使用@Lookup注解来修饰一个抽象方法,该方法会返回bean的实例。
    下面代码运行结果和上面使用ApplicationContext一样。

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Lookup;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public abstract class DemoController {
        @Lookup("bean1")
        public abstract MyBean createBean() ;
    
        @RequestMapping(value = "/")
        public String index(){
            return createBean().toString();
        }
    }

    六、AOP注解

    实现AOP功能使用AspectJ注解
    1、需要在pom.xml加入依赖:

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
            </dependency>

    2、新建一个业务类 TestServiceImpl.java

    package com.example.demo;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestServiceImpl {
        public void testService(){
            System.out.println("要代理的业务方法");
        }
    }

    3、新建一个代理类 ProxyService.java

    package com.example.demo;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class ProxyService {
        @Before("execution(* com.example.demo.*ServiceImpl.*(..))")
        public void before(){
            System.out.println("业务方法调用前执行");
        }
        @After("execution(* com.example.demo.*ServiceImpl.*(..))")
        public void after(){
            System.out.println("业务方法调用后执行");
        }
    }

    4、修改启动类方法 DemoApplication.java

    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    @SpringBootApplication
    public abstract class DemoApplication  {
        public static void main(String[] args) {
            //SpringApplication.run(DemoApplication.class, args);
            new SpringApplicationBuilder(DemoApplication.class).properties(
                    "spring.aop.proxy-target-class=true"
            ).run(args);
        }
    }

    5、控制器 DemoController.java

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class DemoController {
    
        @Autowired
        TestServiceImpl testService;
    
        @RequestMapping(value = "/")
        public String index(){
            testService.testService();
            System.out.println("TestServiceImpl的class:" + testService.getClass());
            return "";
        }
    }

    浏览器访问:http://localhost:8080/,控制台中输出:
    业务方法调用前执行
    要代理的业务方法
    业务方法调用后执行
    TestServiceImpl的class:class com.example.demo.TestServiceImpl$$EnhancerBySpringCGLIB$$2a53cdeb

    七、ComponentScan注解

    ComponentScan注解主要用于检测使用@Component修饰的组件,包括间接使用@Component的组件(如@Service、@Repository、@Controller
    ),并把它们注册到Spring容器中。

  • 相关阅读:
    wince5.0 key
    CSS基础知识思维导图xmind
    切图
    头部标签集
    List和Set的区别
    PicGo + Gitee 实现 Markdown 图床
    GitHub Pages 与 Gitee Pages 上的 Jekyll
    Agile, CI/CD,DevOps
    你还不了解DevOps? 看这篇就够了
    DevOps实践心得
  • 原文地址:https://www.cnblogs.com/gdjlc/p/11600975.html
Copyright © 2011-2022 走看看