Java框架之Spring(三)
本文主要介绍Spring中,
1 Bean 的 init-method 和 destroy-method
2 集合类型的装配
3 注解方式装配
4 以自动扫描把组件纳入spring容器中管理
5 代理模式
一、Bean 的 init-method 和 destroy-method
以前在学Servlet的时候,有 init 和 destory 等时机,用Spring管理的bean 也可以有类似的时机,对于单实例的Bean ,在创建的时候会调用 initAAA() 方法,在销毁的时候,会调用 destroyAAA()。
<bean name="userAction_name" class="cat.action.UserAction" init-method="initAAA" destroy-method="destroyAAA" >
public class UserAction { public void initAAA(){ System.out.println("initAAA调用了,这是在初始的时候调用的"); } public void destroyAAA(){ System.out.println("destroyAAA调用了,这是在销毁的时候调用的"); } }
ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); //会调用 initAAA() ctx.getBean("userAction_name"); ctx.close(); //销毁容器 会调用destroyAAA()
//对于多实例的bean <bean name="userAction_name" class="cat.action.UserAction" init-method="initAAA" destroy-method="destroyAAA" scope ="prototype" > ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); ctx.getBean("userAction_name"); //调用 initAAA() ctx.close(); //销毁容器 不会会调用destroyAAA() //因为对多实例的bean spring 在创建之后,就不再对它的生命周期负责
二、集合类型的配置
1)Set集合
public class UserAction { private Set<String> mySet ; //由Spring把这个集合的内容注进来 public void execute(){ for(String s:mySet){ System.out.println(s); } } public void setMySet(Set<String> mySet) { this.mySet = mySet; } } //配置文件 <bean name="userAction_name" class="cat.action.UserAction" > <property name="mySet"> <set> <value>李白</value> <value>唐太宗</value> <value>小杜</value> </set> </property>
2)List 集合
//和Set完全相同 只不过是上面的中的 set 要完全改成 list <set> <value>李白</value> <value>唐太宗</value> <value>小杜</value> </set>
3) Properties 集合
public class UserAction { private Properties myProps; //不要忘了生成set方法 public void execute(){ Set<String> keySet= myProps.stringPropertyNames(); Iterator<String> it=keySet.iterator(); while(it.hasNext()){ String key=it.next(); System.out.println(key+":"+myProps.getProperty(key)); } } ... }
<bean name="userAction_name" class="cat.action.UserAction" >
<property name="myProps">
<props>
<prop key="key1">夏天</prop>
<prop key="key2">秋天</prop>
<prop key="key3">冬天</prop>
<prop key="key4">春天</prop>
</props>
</property>
</bean>
4) map集合
public class UserAction { private Map<String,String> myMap; //要生成set 方法 public void execute(){ Set<Map.Entry<String, String>> entrySet = myMap.entrySet(); Iterator<Map.Entry<String, String>> it= entrySet.iterator(); while(it.hasNext()){ Map.Entry<String, String > item= it.next(); System.out.println(item.getKey()+":"+item.getValue()); } }
<bean name="userAction_name" class="cat.action.UserAction" >
<property name="myMap">
<map>
<entry key="m_1" value="AK-47"></entry>
<entry key="m_2" value="M_16"></entry>
<entry key="m_3" value="M_P5"></entry>
</map>
</property>
</bean>
三、注解方式装配
附 : p 这个名称空间的作用就是简化属性的编写
<bean name="userInfo" class="cat.beans.UserInfo" >
<property name="id" value="1"></property>
<property name="userName" value="赵强"></property>
<property name="password" value="admin"></property>
<property name="note" value="这是备注"></property>
</bean>
上面的等价于:
<bean name="userInfo" class="cat.beans.UserInfo" p:id="1" p:userName="赵强" p:note="这是备注" p:password="admin123" > </bean>
1) 引入jar包 common-annotations.jar
2) 引用入名称空间 context
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" //这是引入的名称空间 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
//上面这个是名称空间所对应的xsd文件的路径 <context:annotation-config /> //把Spring 对注解的处理器注册到Spring中 <bean name="userAction_name" class="cat.action.UserAction" /> <bean name="userDao_name" class="cat.dao.UserDaoImpl" /> </beans>
public class UserAction { @Resource //用这个注解,进行注入 private IUserDao dao; public void execute(){ dao.addUser(); dao.updateUser(); } }
说明:
1.@Resource 是 javax.annotation.Resource 包下的
2.@Resource 默认是按名称进行装配,再按类型进行装配(在不指定名称的情况下),如果指定了名称.它就严格的按名称进行装配
3.这个注解也可以写在set方法上
//下面的例子,使用了名称 public class UserAction { @Resource(name="xxxxxx_dao") private IUserDao dao; public void execute(){ dao.addUser(); dao.updateUser(); } }
<bean name="userAction_name" class="cat.action.UserAction" /> <bean name="xxxxxx_dao" class="cat.dao.UserDaoImpl" /> //它会被注入 <bean name="oracleDao" class="cat.dao.UserDaoOracleImpl" />
4.@Autowired 注解和 @Resource 功能类似,它是在 org.springframework.beans.factory.annotation.Autowired 默认是按类型装配 ,默认情况下,它要求依赖对象必须存在 ,如果对象是null 值 ,可以设置它的 required=false ,如果也想按名称装配 ,要和另一个注解一起使用
//例子 使用 @Autowired public class UserAction { @Autowired(required=false) @Qualifier("oracleDao") private IUserDao dao; //没有给它生成get 和 set 方法 public void execute(){ dao.addUser(); dao.updateUser(); } }
四、以自动扫描把组件纳入spring容器中管理
开启自动扫描的方式
<context:component-scan base-package="cat.beans" /> <context:component-scan base-package="cat.dao" /> //如果用这种方式开启了自动扫描,就不用加 <context:annotation-config />
它会自动在包下查找类 并纳入Spring管理,包扩子包。它会自动找 带有 @Service @Controller @Repository @Component 的类
==@Service 用于业务层
==@Controller 用于控制层
==@Repository 用于数据访问层
==@Component 用于其他
目前只是一种规范,实际上用哪个都可以
说明:
1) 可以指定bean的名称
@Controller("userAction_name") //传参,指定名称 public class UserAction { ... }
2) 默认情况下,这样配置的bean是单例的,如果是多例
@Controller("userAction_name") @Scope("prototype") public class UserAction { }
//例子 控制层 @Controller("userAction_name") @Scope("prototype") public class UserAction { @Resource(name="userDaoOracleImpl") private IUserDao dao; public void execute(){ dao.addUser(); dao.delUser(); } } //数据访问层 @Repository public class UserDaoOracleImpl implements IUserDao { public void addUser() { System.out.println("addUser方法被调用了Oracle版的 "); } public void delUser() { System.out.println("delUser方法被调用了Oracle版的 "); } public void updateUser() { System.out.println("updateUser方法被调用了Oracle版的 "); } } //测试 ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserAction action =(UserAction) ctx.getBean("userAction_name"); action.execute(); //对于纳入自动扫描的bean ,它的名称默认是类名, 首字母小写
注意:使用了注解方式以后,原来的初始化和销毁方法应该怎么配置呢?
@Component @Scope("prototype") public class UserAction { ... @PostConstruct //初始方法 public void initAAA(){ System.out.println("初始化的方法被调用了"); } @PreDestroy //销毁方法 public void destoryAAA(){ System.out.println("销毁的方法被调用了"); }
五、代理模式
因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗?
你有想过限制访问某个对象,也就是说,提供一组方法给普通用户, 特别方法给管理员用户?以上两种需求都非常类似,并且都需要解决一个更大的问题:你如何提供一致的接口给某个对象让它可以改变其内部功能,或者是从来不存在的功能? 可以通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。即代理对象。它可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。
业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点
静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前,就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
//1) 接口 public interface IUserDao { void addUser(); void delUser(); void updateUser(); void searchUser(); } //2) 实现类 //委托类 public class UserDaoImpl implements IUserDao{ public void addUser() { System.out.println("addUser方法执行了"); } public void delUser() { System.out.println("delUser方法执行了"); } public void updateUser() { System.out.println("updateUser方法执行了"); } public void searchUser() { System.out.println("searchUser方法执行了"); } }
//3) 代理类 public class UserDaoProxy implements IUserDao { private UserDaoImpl userDaoImpl=new UserDaoImpl(); //代理类中,要有一个被委托的类的实例对象 private String path="log.txt"; public void addUser() { try { userDaoImpl.addUser(); BufferedWriter bw=new BufferedWriter(new FileWriter(path)); bw.write(new Date()+": 执行了添加用户操作 "); bw.newLine(); bw.close(); } catch (IOException e) { e.printStackTrace(); } } public void delUser() { try { userDaoImpl.delUser(); BufferedWriter bw=new BufferedWriter(new FileWriter(path)); bw.write(new Date()+": 执行了删除用户操作 "); bw.newLine(); bw.close(); } catch (IOException e) { e.printStackTrace(); } } public void updateUser() { try { userDaoImpl.updateUser(); BufferedWriter bw=new BufferedWriter(new FileWriter(path)); bw.write(new Date()+": 执行了更新用户操作 "); bw.newLine(); bw.close(); } catch (IOException e) { e.printStackTrace(); } } public void searchUser() { long begin=System.currentTimeMillis(); userDaoImpl.searchUser(); long end=System.currentTimeMillis(); System.out.println("查询用户一共用了:" +(end-begin) +"ms"); } }
//3) 代理工厂 public class static UserDaoProxyFactory { public IUserDao getUserDao(){ return new UserDaoProxy(); } }
//4) 测试 public static void main(String[] args) { IUserDao dao=UserDaoProxyFactory.getUserDao(); dao.addUser(); dao.updateUser(); dao.delUser(); dao.searchUser(); }
静态代理的缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。