目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情,总之不对的地方,多多指出,我们一起徜徉代码的海洋!
 
我这里做每个章节去说的前提,不是一定很标准的套用一些官方名词,目的是为了让大家可以理解更加清楚,如果形容的不恰当,可以留言出来,万分感激

1、注解的基础概念

什么是注解编程?

指的是在类或者方法上加入特定的注解(@XXXX),完成特定功能的开发。

2、为什么要讲解注解编程?

  • 注解开发非常方便,代码简洁
  • 开发速度大大提高
  • Spring开发一个潮流

Spring从2.x开始引入注解,Spring3.x开始完善注解,Springboot推广开来,普及注解。

很多人私信我,为啥现在xml的方式不用了,早不讲注解?

我个人觉得,不能混合讲,容易混乱,应该逐个击破!!

3、注解作用

  • 替换xml这种配置形式,简化配置

从配置文件的方式,转换成注解的方式创建对象

  • 替换接口,实现双方调用的契约性

假设我有这么一个图

图中有两个角色,功能提供者和功能的调用者,对于功能提供者有个m1方法,在功能的调用者中的invoke,可以调用提供者的m1方法,进而完成用户的需求,这个流程看似是没有任何问题的。

但是,如果功能的调用者,和功能的提供者,不是一个人开发的;A程序员写了Consumer,B程序员写了Provider,这两个程序员可能今生素未谋面,那A程序员,怎么会知道B程序员写了Provider提供者,里面的m1方法给我调用呢?

有人说,这个情况会发生吗?当然有可能,如果此时这个调用者是Spring框架,功能的提供者是你,那Spring怎么知道若干年后,你会写一个这个m1方法给我去调用呢?

显然Spring是不知道这个事情的,那么就需要在调用者,和被调用者之间达成一个约定,这就是契约性,这个是可以由接口来保证的!!

再看一张图

这个图出现了一个Contract接口,里面有method1接口方法,那么若干年后的你,作为一个程序员,你只需要实现这个契约规则,你实现了这个接口,和里面的method1方法,那么你作为调用者,是不是可以得到这个Contract接口的实现类,从而调用

method1方法。这样是不是,二者从不认识,素未谋面,但是有了这个接口的存在,你就可以实现你们直接的互通和业务的实现?

虽然通过接口的方式可以实现功能调用者和被调用者之间的约定,但是你发现,如果接口里面有很多很多方法,作为一个素不谋面的人,也要实现这接口里的所有方法,比如说,在开发web中,我们要写Servlet;

public interface Servlet {void init(ServletConfig var1) throws ServletException;ServletConfig getServletConfig();void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;String getServletInfo();void destroy();
}

我们要实现Servlet里面的很多方法,这个Servlet接口,实际上就达成了Tomcat和我们程序员之间的约定!!

程序员写的代码由tomcat去调用,二者要达成一个约定,让tomcat知道要调用你什么,也就通过Servlet达成了一个约定。程序员写Servlet,Tomcat调用Servlet。

但是你开发你会发现一个问题,其实在整个Servlet开发中,你最关心的应该是service方法,其他的方法你基本上不太会用到,但是你都要去实现一遍!!于是有了设计模式——适配器设计模式(感兴趣自己了解下),程序员不再去实现Servlet,而是实现

HttpServlet,而这种解决也是比较繁琐的,所以在注解诞生后,通过注解,也可以体现这种契约关系。用起来比接口更加方便点!

再看一张图

此时我通过注解@Contract,当然你可以叫其他注解,这个只是举个例子,只要你是调用者来调用提供者的方法,那对应的提供者的方法上,都给你加上一个注解@Contract,后续我们的调用者,就可以通过反射的方式,拿到这个注解信息,

如果我发现某个方法上有这个注解信息,就意味着是我最终要调用的对象,就可以通过达成共识!这个是不是就不需要强制实现接口里的所有方法?

上面的例子都是为了更好理解注解编程带来的好处!

回顾我们第一版讲aop的时候,是不是额外功能要实现MethodInteceptor的invoke方法,实际上这个就是用接口达成的一个契约!

而后面衍生出的注解,我们额外功能,就不需要实现任何接口,对于你要的额外功能,你只需要在你的额外功能上,加上@Around注解,Spring也就和你达成契约关系。

所以注解成为了主流的方式!

4、Spring注解的发展历程

Spring2.x开始支持注解编程,提供@Component,@Service,@Scope等等,目的是为了在某些情况下,简化Xml的配置,作为Xml的有益补充。

Spring3.x提供了更多的注解,@Configuration,@Bean等等,目的是想彻底取代Xml,基于纯注解配置。

Spring4.x 衍生出了一个跨时代的框架,Springboot,提倡使用注解进行开发!

5、Spring注解开发的一个问题

Spring基于注解的配置后,是否还能解耦合呢?我们知道之前的Xml是为了解耦,不需要将对象的管理,耦合在代码中,并且配置文件的方式不会被编译,达到了解耦的目的。

但是这个时候,你出来了注解,是写在代码上的,那还能达到解耦的目的吗?

你能想到这问题,实际上Spring也能想到这个。

所以,在Spring框架上应用注解时候,如果对注解的配置内容不满意,可以通过Spring的配置文件进行覆盖的。最终你会发现,你改的还是配置文件。依旧不会存在耦合

6、Spring的基础注解

6.1  对象创建相关注解

搭建开发环境

我们要在配置文件中添加这样一个标签

<context:component-scan base-package=""></context:component-scan>

作用是:让Spring框架去扫描关于创建对象的注解,这样就可以让Spring知道我们创建对象的注解存在的位置。在base-package中写包名,就意味这个包,及其子包下都会被Spring扫描注解,进而让注解生效。

来看下相关对象创建的注解

  • @Component注解

这个注解可以替换bean标签<bean id="" class=""/>,来创建对象,那这个注解怎么体现这个id和class呢?

class拿到的是全限定名,既然这个注解在这个类上,我们就可以通过反射的方式,拿到这个注解对应的类,从而拿到全限定类名

id属性,作为Component注解,有个默认体现id的形式,就是这个类名,首单词首字母小写,如果是User,默认的id是user,如果是UserDAO,则是userDAO,和驼峰不是一个概念哈,别搞混了。

后面我就不做代码演示了,这个前面都有的!

@Component
public class User implements Serializable {private Integer id;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}
    @Testpublic void test11(){ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");com.chenxin.spring5.bean.User user = (com.chenxin.spring5.bean.User) ctx.getBean("user");System.out.println("user = " + user);}

这个测试很简单

2021-04-18 21:45:16 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'userService'
2021-04-18 21:45:16 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'userDao'
2021-04-18 21:45:16 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
2021-04-18 21:45:16 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'dataSourceTransactionManager'
user = com.chenxin.spring5.bean.User@20b2475a

当然你也可以显示的定义id名字,比如@Component(value="u"),getBean的时候,getBean("u")即可!

上面我们提到,我们如果对注解不满意,是可以通过配置文件的方式,来覆盖注解的配置,这个我们怎么做到的呢?

既然我们刚刚写了这个标签component-scan,只是告诉Spring你要去扫注解的位置是在哪些包下:

<context:component-scan base-package=""></context:component-scan>

那你创建对象,是不是还可以通过bean标签的方式?如果我们想用配置文件的方式来覆盖注解,那么设置这个bean的id值,就得让注解和配置文件的id值保持一致!!!

即这样

<context:component-scan base-package="com.chenxin.spring5.bean"></context:component-scan><bean id="user" class="com.chenxin.spring5.bean.User"></bean>@Component//不写id,默认是类的首字母小写
public class User implements Serializable {private Integer id;private String name;public String getName() {return name;}
}

看下日志有没有差异,注意有个Overriding,意思是覆盖了bean的定义,取代了Class的User这个类创建的bean对象

2021-04-18 21:54:55 DEBUG DefaultListableBeanFactory:906 - Overriding bean definition for bean 'user' with a different definition: replacing [Generic bean: class [com.chenxin.spring5.bean.User]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:\spring专题\spring-day03\target\classes\com\chenxin\spring5\bean\User.class]] with [Generic bean: class [com.chenxin.spring5.bean.User]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]]

如果你写了创建对象的id不一致,那么Spring会帮你创建两个bean对象,这个你在开发中,如果你要注入的话,很有可能会报错bean冲突!!这个要注意

所以这样我们就使用了标签覆盖了注解的内容!

实际上Spring也为@Component这个注解,衍生了很多的注解

比如:

  • @Controller
  • @Service
  • @Repository

注意,本质上,这些衍生的注解就是@Component,作用,细节,用法都是完全一致的,在这些注解上,加上了个@Component注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {@AliasFor(annotation = Component.class)String value() default "";
}@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {@AliasFor(annotation = Component.class)String value() default "";
}@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {@AliasFor(annotation = Component.class)String value() default "";
}

是不是完全一毛一样??

那这些注解存在的意义在哪里呢?Spring衍生这些注解的意义,在于更加准确的表达一个类型的作用,比如

@Repository,我们建议用在Dao层

@Controller,建议用在控制层Controller

@Service,建议用在业务层Service上,这些都是Spring建议给我们的。

最后注意一点,在Spring整合Mybatis开发的过程中,不适用@Repository,为什么?因为我们上节讲了Spring整合Mybatis的过程中,不会写Dao的类,也就是不会写XXXDaoImpl,这个动态创建的代理对象,是不是通过MapperScannerConfigurer

帮我们动态创建的,所以我们没有机会加这个注解。

  • @Scope注解

作用:用户控制简单对象创建次数

这个我们之前也是接触过的,bean中有这么一个属性叫做scope="singleton|prototype"

自然而然我们也可以用这个注解表示对象创建的次数,是创建一次,还是每一次从工厂创建是新的对象?

所以@Scope注解,等同于scope="singleton|prototype"

@Component//不写id,默认是类的首字母小写
@Scope("singleton") | @Scope("prototype")
public class User implements Serializable {private Integer id;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}
    @Testpublic void test12(){ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");com.chenxin.spring5.bean.User user = (com.chenxin.spring5.bean.User) ctx.getBean("user");com.chenxin.spring5.bean.User user1 = (com.chenxin.spring5.bean.User) ctx.getBean("user");System.out.println("user = " + user);System.out.println("user = " + user1);}

如果不添加这个注解,或者你添加了这个注解,没标明singleton还是prototype,默认都是单例!!!

这里我就不对这个注解做演示, 感兴趣的自己测试下哈!!

  • @Lazy注解

作用:延迟创建单实例对象

之前我们说过,如果我们在bean标签中,想让这个对象在工厂创建的时候不被创建,而是在getBean的时候被创建,我们就需要通过一个属性lazy="true",来实现

<bean id="user" class="com.chenxin.spring5.bean.User" lazy-init="true"></bean>

但是注解后,取而代之用的就是@Lazy,默认是true,在要创建的类上加上这个注解,表示这个类的对象在getBean的时候才会被创建出现

@Component//不写id,默认是类的首字母小写
@Scope("prototype")
@Lazy
public class User implements Serializable {private Integer id;private String name;public User() {System.out.println("User.User");}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}

我也不多做测试了,这个留给大家做家庭作业哈!

6.2  生命周期方法相关注解

之前我们在讲Bean的生命周期的时候,讲解了两个比较重要的方法,一个是初始化相关的方法,一个是销毁方法

对于初始化方法我们需要去写一个类,并实现InitialazingBean接口,或者是我们自己实现了一个初始化方法,让Spring的配置文件帮我们找到这个初始化方法,也就是bean中的init-method这个属性,写上我们的初始化方法

与之对应的销毁方法,一种是实现DisposeBean接口,一种是在bean标签中定义相关属性destroy-method,写上需要销毁的方法。

应用注解后,我们可以用注解取代这两个属性实现的功能

  • @PostConstruct--------初始化
  • @PreDestroy----销毁
@Component
public class Product {@PostConstructpublic void myInit(){System.out.println("Product.myInit");}@PreDestroypublic void myDestroy(){System.out.println("Product.destroy");}
}

测试下

    @Testpublic void test13(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");com.chenxin.spring5.bean.Product product = (com.chenxin.spring5.bean.Product) ctx.getBean("product");ctx.close();}

结果

2021-04-18 22:47:51 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
2021-04-18 22:47:51 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'product'
Product.myInit
2021-04-18 22:48:13 DEBUG ClassPathXmlApplicationContext:987 - Closing org.springframework.context.support.ClassPathXmlApplicationContext@6dde5c8c, started on Sun Apr 18 22:47:50 CST 2021
Product.destroy

结果很明确,在构造方法后执行初始化操作,关闭工厂前进行对象的销毁!!

注意:这两个注解,并不是Spring给提供的,而是一个JSR-520规范提供的,所谓的JSR是J2EE的规范,只不过对于Spring来说,他很好的兼容了JSR,所以是可以在Spring中使用的

上面我们再一次验证了,通过注解实现了接口的契约性!!!

声明周期相关的注解,我就讲解完成了

6.3 注入相关的注解

上面讲了创建对象的相关注解,那么赋值什么的,是不是就是注入,所以本节讲解注入相关

注入,有两种类型的注入,一个是用户自定义类型的注入(@Autowired),一个是JDK类型的注入(),以前我们在讲解UserServiceImpl注入Dao对象的时候,先在xml把这两个对象创建出来,然后在相关UserServiceImpl中创建set方法,因为后面需要注入

进来,然后进行

private UserDao userDao;进行属性的赋值,既然Autowired这个注解的意义是,让Spring帮我们注入属性,那现在我们是不是只要把@Autowired注解,放在set方法上,就可以了?因为之前我们需要在配置文件里赋值的前提是,要具备set方法,才可以进行赋值,注入!!

  • Autowired注解

实现

public interface UserService {public void save();}@Service
public class UserServiceImpl implements UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {//为了证明是调用set方法注入System.out.println("UserServiceImpl.setUserDao");this.userDao = userDao;}public void save() {userDao.save();}
}

dao层

public interface UserDao {public void save();
}@Repository
public class UserDaoImpl implements UserDao {public void save() {System.out.println("UserDaoImpl.save");}
}

测试类:

    @Testpublic void test14() {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");com.chenxin.spring5.injection.UserService userService = (com.chenxin.spring5.injection.UserService) ctx.getBean("userServiceImpl");userService.save();}

很明显我们的确把@Autowired注解放在set方法上后,Spring就会帮我们把属性注入进去,其本质也是set注入

使用细节:

我们有个类是UserDaoImpl,如果还有其他的类比如ProductDaoImpl,难么Spring是怎么帮我们找到哪个类需要注入的呢?这就是@Autowired的属性的特征,他是基于类类型进行注入,必须是成员变量相同的类型,或者是我的子类,乃至接口的实现类。

1、Autowired基于类型注入,必须与目标成员变量相同的类型,或者是我的子类,乃至接口的实现类。

2、Autowired可以基于名字(对象的id值)注入,但是要配合一个注解去使用,这个注解就是@Qualifier。

  • @Resource注解

举个荔枝,UserServiceImpl,需要注入UserDaoImpl这个类,我们加上@Repository注解,UserDaoImpl在Spring工厂中的id值是默认首单词首字母小写,即userDaoImpl,我只要加上个@Qualifier注解,就可以基于名字的注入,后期我想改成什么样的bean名字,我都可以以这样的方式实现。

更推荐第一种的方式,基于类型的注入。第二个了解即可!

3、Autowired放置位置:对应成员的set方法上,还可以直接放置在成员变量上!!区别在哪里呢?前者是通过set注入的方式进去,后者实际上通过反射,拿到这个私有成员变量,直接赋值,而不调用set方法。

有人该问了,如果我既在set上,又在成员变量上,都放了这个Autowired注解,会默认走set注入的方式。自己可以试试,原理很简单,我可以拿到set方法,用对象的方式,但是我要直接为私有属性赋值,我要通过反射,你说哪个划算吧?

当然,@Qualifier注解,也一样可以配合在属性上进行byName注入,所以开发用的最多的,也是在属性上加Autowired。

4、对于Autowired和Qualifier,都是Spring为我们提供的注解,作为JavaEE规范,也为我们提供了类似功能的注解,就是我们说的@Resource(name="")基于名字注入,在JSR-250规范中实现的。

所以有个等价关系:

@Service
public class UserServiceImpl implements UserService {@Autowired@Qualifier("userDaoImpl")private UserDao userDao;public void setUserDao(UserDao userDao) {System.out.println("UserServiceImpl.setUserDao");this.userDao = userDao;}public void save() {userDao.save();}
}@Service
public class UserServiceImpl implements UserService {@Resource(name = "userDaoImpl")private UserDao userDao;public void setUserDao(UserDao userDao) {System.out.println("UserServiceImpl.setUserDao");this.userDao = userDao;}public void save() {userDao.save();}
}

这里有个小细节,如果我@Resource不写name呢,或者这个注解没有按照名字配对成功?实际上这个时候,@Resource就自动转换成,通过类型注入了,给个案例你看看

在UserServiceImpl中注入的时候,用@Resource没有指定name的值,也就是没通过id找,正巧我把dao的bean的id名字也改成了一个userDaoImpl1,结果也是可以注入

@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserDao userDao;public void setUserDao(UserDao userDao) {System.out.println("UserServiceImpl.setUserDao");this.userDao = userDao;}public void save() {userDao.save();}
}@Repository("userDaoImpl1")
public class UserDaoImpl implements UserDao {public void save() {System.out.println("UserDaoImpl.save");}
}

所以这个@Resource注解十分的强大!!!

在JSR-330中给我们又定义了一个注解是@Inject,这个注解和@Autowired是完全一致的,都是基于类型注入的,但是我们没办法直接使用,因为这个不是Spring的,所以你想用的话,要引入对应的坐标

<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version>
</dependency>

这个应用在EJB3.0比较多,但是EJB现在不是黄了嘛,所以你可以不去使用,但是你不能不知道这个注解!!

下面我们继续看JDK类型的相关注解

6.4 JDK相关的注解

我们刚刚说到,创建对象我们从xml的bean标签方式,已经转变到了注解@Component的方式,但是这个对象是帮我们创建出来了,xml中我们可以在bean中定义property标签,为我们的属性进行赋值,一旦xml中的bean标签创建对象,被注解替代了,那么这些属性的赋值,应该如何去处理呢?

其实,Spring已经帮我们提前考虑好了,既然是赋值,xml的bean不存在了,那么肯定也不能写在代码里,一旦写了,就耦合了,那就违背了Spring的思想,于是Spring这么做:

定义一个xxxx.properties配置文件,以key,value的形式来进行属性的赋值,那么Spring如何读取这个配置文件,来实现属性赋值这个效果呢?

假设我有个User对象,里面有两个属性

public class User implements Serializable {private Integer id;private String name;public User() {System.out.println("User.User");}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}

前几集的时候我讲过在整合jdbc的时候,要想Spring来读取你自定义的配置文件,是不是通过一个xml的配置:

<context:property-placeholder location="classpath:/xxxx.properties"></context:property-placeholder>

让Spring可以读取你配置文件的地方,紧接着我们肯定要通过注解的方式,让User这个类中的属性,读取到properties文件的值,进而为属性进行赋值操作?

Spring提供了@Value这个注解,来搞定对于配置文件赋值给属性的操作,只需要在你要赋值的属性上,加上这个@Value这个注解就可以了

@Component
public class User {@Value("${id}")private Integer id;@Value("${name}")private String name;@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

当然前提你要定义一个配置文件,我放在resource下,叫做application.properties

id = 2
name=chenxin

测下:

    @Testpublic void test15() {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");com.chenxin.spring5.jdk.User user = (com.chenxin.spring5.jdk.User) ctx.getBean("user");System.out.println(user.toString());}

很明显是这样的

2021-04-19 22:47:50 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
2021-04-19 22:47:50 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'user'
2021-04-19 22:47:50 DEBUG PropertySourcesPropertyResolver:115 - Found key 'id' in PropertySource 'localProperties' with value of type String
2021-04-19 22:47:50 DEBUG PropertySourcesPropertyResolver:115 - Found key 'name' in PropertySource 'localProperties' with value of type String
User{id=2, name='chenxin'}

但是其实你发现没,这个还是没脱离开xml,因为有了这个标签

<context:property-placeholder location="classpath:/xxxx.properties"></context:property-placeholder>

所以我们是不是想Spring也应该把这个标签,用注解的方式也能替代下?当然你猜对了 ,这个注解是@PropertySource:替换了Spring配置文件中的context:property-placeholder这个标签

于是去掉了xml这个配置以后,代码我们加上这个注解

@Component
@PropertySource("classpath:application.properties")
public class User {@Value("${id}")private Integer id;@Value("${name}")private String name;@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

这个location的内容,就是PropertySource的内容,注意,没有空格哈,不然会报错的!!!

注意细节:

1、@Value不能作用在静态属性上,因为java基础告诉我们,静态属性你在编译的时候,JVM就给你分配内存了,而且静态的属性,是跟着类走的,属于类变量;而局部变量是先有对象,被new出来,才有属性这么一说,所以@Value这个注解,本身是Spring

的属性注入,本身应该是先有这User对象,才能给你实例化,静态属性都不需要实例化,直接类.的方式就使用了,就没有创建对象和生命周期这一说,何来注入呢?再从另一个角度来说,Spring注入的本质,除了反射,就是set注入,set方法是对象的方法,

不属于类方法,所以Spring注入要求是对象的注入,对象的属性注入,而不是类属性注入,类变量注入。

2、@Value注解+@PropertySource这个方式,不能注入集合类型。

在之前说过,xml中可以通过集合标签为属性赋值,但是换成了@Value后,就无法表示了,有人说,如果是list,你可以通过逗号隔开啊,那你怎么知道我给的是数组,还是集合?所以Spring就不支持了。如果在开发中遇到这个场景怎么办?

Spring提供了新的配置形式,就是YML/YAML文件的形式,后续我们Springboot的讲解会详细讲解这个,这里先了解这个注意事项!!

明天我继续更新第十章:注解编程——下,希望大家多多关注!!

Spring基础专题——第九章(基础注解编程——上)相关推荐

  1. xmm1是什么器件_电路基础9第九章.ppt

    电路基础9第九章 第9章 Multisim 2001 在电路分析中的应用 9.1 Multisim 2001的基本使用方法 9.2 Multisim 仿真实验内容 9.1 Multisim 2001的 ...

  2. 《从0到1》笔记 第九章 基础决定命运

    第九章 基础决定命运 ----每个成功企业都是独一无二的,而要做好每个事业,有些事情在起步阶段就必须做好: ----基础没有打好的初创企业是无法挽救的. 开头很特殊,它在本质上有别于之后的阶段. 如美 ...

  3. 信息学奥赛一本通(C++版) 第二部分 基础算法 第九章 动态规划

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 第二部分 基础算法 第九章 动态规划 第一节 动 ...

  4. spring controller 增加header字段forward_Spring 注解编程之模式注解

    上篇文章研究 Spring XML Schema 扩展进制,这段时候一直研究 Spring 注解编程的原理.原本以为有了之前研究基础,可以很快理解注解编程原理.没想到这个过程非常困难,注解编程源码难度 ...

  5. Dubbo基础专题——第一章(带你认识Dubbo)

    前言:刚完成的Spring基础专题本想更新源码的,但是发现分布式非常火,而我喜欢玩这个,所以今年我希望把我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个 ...

  6. Dubbo基础专题——第二章(Dubbo工程简单实践)

    前言:刚完成的Spring基础专题本想更新源码的,但是发现分布式非常火,而我喜欢玩这个,所以今年我希望把我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个 ...

  7. 前端基础 HTML 第九章 使用框架结构 ----暑假学习第五天

    第九章 使用框架结构 9.1 框架的基本概念 框架是浏览器窗口中的一个区域,它可以显示与浏览器窗口的其余部分中所显示的内容无关的HTML文档 框架集是HTML文件,它定义一组框架的布局和属性,包括框架 ...

  8. 鸟哥的Linux私房菜(基础篇)- 第九章、文件与文件系统的压缩与打包

    第九章.文件与文件系统的压缩与打包 最近升级日期:2009/08/20 在 Linux 底下有相当多的压缩命令可以运行喔!这些压缩命令可以让我们更方便从网络上面下载大型的文件呢!此外,我们知道在 Li ...

  9. 利用Spring扩展点模拟MyBatis的注解编程「知识点多多」「扩展点实战系列」- 第448篇

    历史文章(文章累计440+) <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> <国内最全的Spring Boot系列之三> ...

最新文章

  1. [css] style标签写在body前和body后的区别是什么?
  2. pb利用datawindow查询符合条件的数据并且过滤掉其他数据_数据质量监测
  3. ecshop 收货人信息电话必填改为手机必填
  4. CVPR 2021 | 腾讯AI Lab入选论文解读
  5. python基础—正则表达式即re模块
  6. Ubuntu 14.04/16.04 (使用apt-get进行安装) 安装Docker
  7. 趣味程序之数学之美系列
  8. nginx编译安装和yum安装那个更好?
  9. 机器学习基石(笔记)
  10. Jquery头像编辑器
  11. UCOSIII---工程移植
  12. 5G+北斗融合定位技术介绍
  13. 那些常被忽略的 html 标签
  14. 蛮荒搜神记服务器在维护,蛮荒搜神记法宝洗练图文教程 蛮荒搜神记如何提升战斗力?-游侠网...
  15. Linux之创建组名和删除组名
  16. 统俗讲义之——何为统计显著性(Statistical Significance)
  17. Strerror函数和Perror函数的介绍及使用
  18. linux系统安装hba驱动,RedHat Linux下的HBA驱动安装
  19. 嵌入式开发中的防御性C语言编程
  20. 乐华娱乐IPO搁浅:王一博是旗下艺人 CMC阿里字节是股东

热门文章

  1. AIphaCode 并不能取代程序员,而是开发者的工具
  2. 如何利用 Python 爬取 LOL 高清精美壁纸?
  3. WebDriver 识别反爬虫的原理和破解方法~
  4. 告别手敲 SQL ?GPT-3 自动帮你写
  5. RISC-V 正在成为芯片世界中的 Linux
  6. 再见了,Python!!
  7. 4000个“不会数学”的程序员出现大反转!居然能学AI,玩算法,搞逻辑!背后原因首次曝光...
  8. 为何Google、微软、华为将亿级源代码放一个仓库?从全球最大代码管理库说起...
  9. NLP机器翻译深度学习实战课程基础 | 深度应用
  10. 为什么有的机器学习应用公司必将失败?