最近接触了几个比较大的Java项目,其中经常存在Bean之间的依赖,例如在某服务启动前要初始化各种词典,主要方式有显式声明Bean和隐式注解注入2种,本文着重介绍后一种。java
首先咱们定义“依赖”是什么:若是在Class A中有Class B的实例,则称A依赖B。
如今咱们来构造一个依赖:数据库
public class UserDao { DBConnection conn; public UserDao() { // 先初始化数据库链接 conn = new DBConnection(); } }
上述代码中,UserDao
即对DBConnection
存在依赖。但这样构造依赖存在以下问题:
(1) 耦合度高。设想在将来咱们为DBConnection
的构造函数新增字段name
,那么上述代码将修改成:conn = new DBConnection();
conn = new DBConnection(String name);
(2) 若是DBConnection
的初始化耗时很长,将影响构造函数的后续初始化工做。express
上述UserDao
在构造函数里直接初始化DBConnection
的过程,属于主动初始化依赖对象,两者耦合度高,不方便测试。若是被依赖对象事先就初始化好了,直接供给本Class调用,则称之为依赖注入。例如上述代码可改写为以下:app
public class UserDao { @Autowired //依赖注入 DBConnection conn; public UserDao() { } }
要使注入生效,咱们还须要在DBConnection
中添加Bean标识Component
函数
@Component //将类标识为Bean public class DBConnection { public DBConnection() { init(); //初始化工做 } }
同时,为了让Bean的标识被探测到,咱们还须要在xml配置文件中添加自动扫描:测试
<context:component-scan base-package="org.example.service"/>
到此处相信读者大体了解其注入原理:code
@Component
的标识,使之成为Bean。@Autowired
对Bean进行依赖注入。 事实上咱们能够在任何状况下使用@Component
标识须要被"Bean化"的服务。但不久咱们会发现,其余更有水平的代码里会有更多富于变化的标识,例如:@Repository
、@Service
和@Controller
。这是怎么回事?设想咱们如今有加载本地词典的类A,本地词典监控类B,两者有明显区别:前者的功能视数据访问,后者则是监控服务,咱们更愿意对标识作更精细化的区分,具体以下:
@Repository
是为DAO(数据访问)特制的声明,将一个类声明为Repository
意味着该类主要功能是数据读取、DB访问。
@Service
则声明该类为服务性质的Bean,例如上文中的词典监控服务,还有许多无关数据操做的功能性初始化类,都应该被标识为Service
。
@Controller
声明标志着一个类是SpringWeb的MVC控制器,主要负责MVC之间的操纵。另外一个相似的声明是@RequestMapping,将URL映射为一个方法。component
以上3个新的声明均为@Component
的子集,所以仅仅使用@Component
也不影响后续的扫描和注入过程。但在现实生活中,其实只有不多的场合咱们须要用到@Component
,例如没法给类作清晰的定位时。xml
<context:component-scan base-package="org.example.service"/>
注意到上文中的这段自动扫描,它的包名其实能够根据用户需求作出更改。例如:如今我有一个判断query是否为脏词的服务A,它仅依赖注入了自动加载脏词词典的类B。此外,项目中还存在访问数据库的类C,但咱们没有为数据库配置访问信息。当咱们将注解扫描范围设置为整个项目时,启动服务A将产生问题。对象
解决方案是缩小自动扫描范围,使得Spring仅扫描B而不扫描C。具体作法是增长扫描过滤器, Spring支持正则Regex和AspectJ两种表达式的方式进行扫描过滤。
例如:
<context:component-scan base-package="org.example.service"> <context:include-filter type="regex" expression="org\.example\.service\.dictionary\..*"/> <context:exclude-filter type="aspectj" expression="org.example.service.db..*"/> </context:component-scan>
上面的xml配置就成功的扫描名为com.example.service.dictionary
的package而忽略了以org.example.service.db
为前缀的package。
@Autowired
:负责自动装配被成功扫描的Bean。@Qualifier("xxxxx")
: 有时候咱们有参数不一样的两个相同类的bean须要装配(具体为何会存在这种现象?可想象要链接2个数据库。),那么如何单独指定我须要装配哪个呢?@Qualifier
配合@Autowired
可用于消除这类歧义。
在方法内部,咱们也有一些"注解":@PostConstruct
和@PreDestroy
是Bean内分别执行初始化和销毁bean的注解。b>注意!它并不属于Spring,而是属于J2ee里的jar包,所以使用时须要在xml配置:
<context:annotation-config />
@PostConstruct
: 初始化bean@preDestroy
: 结束前清理。