Sping中自定义默认注解名生成器
在类中贴上@Repository等标签,相当于在Spring容器里定义了一个Bean类,这个Bean的名称如果不指定名称,将默认使用当前类名(不含包名)的首字母小写作为Bean的名字.
需求:
要确保这个Bean名称要唯一,显然这样定义的Bean名称太过简单,比较容易造成一样情况,如果能使用全类名作为Bean可防止这样的情况,因为不存在全类名一样的类.例如:org.kirinsoft.frame.AClass
技术实现:
指定定义一个Bean名字生成器,实现Spring提供的BeanNameGenerator,在generateBeanName方法中设计出自己需要的Bean名称.
另外在Spring配置文件中的自动注解配置要指定名字生成器类,如:
<!-- 自动注解配置 --> <context:annotation-config /> <context:component-scan base-package="com.kirinsoft.frame" name-generator="com.kirinsoft.frame.pub.UniqueBeanNameGenerator" />
具体实现方法:
/** * 唯一Bean名称生成器.以类全名作为Bean名称. */ public class UniqueBeanNameGenerator implements BeanNameGenerator{ @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return definition.getBeanClassName(); } }
Spring中事务的作用范围
下面一种情景:
在做项目中通常会进行分层架构.假设可分为Service、Biz、Dao3层.关系如下:
一个Service类调用多个Biz类,一个Biz调用多个Dao类完成业务逻辑.
基于这种情况,通常我们把事务控制到Service层.
很当然的引入Spring中配置:(其他配置略)
<property name="beanNames"> <list> <value>*ServiceImpl</value> </list> </property>
那么,引入一个问题:如果同时对Service层和Biz层加入Spring的事务配置中,是哪一层起作用呢、?
换句话说,假设一个Service有两个Biz类,那么第一个Biz类操作成功,而第二个操作失败,此时第一个Biz是否有回滚呢。?
对应的Spring配置:
<property name="beanNames"> <list> <value>*ServiceImpl</value> <value>*BizImpl</value> </list> </property>
通过实验证明可得到结论:哪个包含的范围大,哪个事务起作用。
上面的假设,Service的事务起作用,第一个Biz会被回滚掉。
Spring事务属性说明
Spring事务属性配置属性说明如下:
PROPAGATION_REQUIRED 【支持当前事务,如果当前没有事务,就新建一个事务。(常用)】 PROPAGATION_SUPPORTS 【支持当前事务,如果当前没有事务,就以非事务方式执行】 PROPAGATION_MANDATORY 【支持当前事务,如果当前没有事务,就抛出异常】 PROPAGATION_REQUIRES_NEW 【新建事务,如果当前存在事务,把当前事务挂起】 PROPAGATION_NOT_SUPPORTED 【以非事务方式执行操作,如果当前存在事务,就把当前事务挂起】 PROPAGATION_NEVER 【以非事务方式执行,如果当前存在事务,则抛出异常】 PROPAGATION_NESTED 【如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作】 -Exception表示有Exception抛出时,事务回滚。-代表回滚+就代表提交 readonly 就是read only, 设置操作权限为只读,一般用于查询的方法,优化作用 |
Spring AOP 之 RegexpMethodPointcutAdvisor
昨天,做了有关日志的AOP,对相关的AOP知识总结如下:
1.引入AOP(Aspect Oroented Programming) 面向切面编程,是消除代码重复的一种方法。
2.Spring AOP 中提供了两种PointcutAdvisor,分别是:
①org.springframework.aop.support.RegexpMethodPointcutAdvisor (需要加上完整类名,可以用Spring提供的匹配方式)
②org.springframework.aop.support.NameMatchMethodPointcutAdvisor(只需要方法名,不用加类名)
今天,主要来说明下RegexpMethodPointcutAdvisor的用法。贴一个例子来说明,一些说明都写在注释中~看贴的代码:
/** * 打印接口 * @author ChenST */ public interface IPrinter { /** 打印接口 */ public void print(); }
/** * 打印接口实现类 * @author ChenST */ @Repository public class PrinterImpl implements IPrinter{ @Override public void print() { System.out.println("hello world"); } }
/** * 通知,在执行方法执行后调用该方法 * @author ChenST */ //对应的还有MethodBeforeAdvice等 public class AfterPrinter implements AfterReturningAdvice{ //第一个参数表示 切入方法的 [返回对象] //第二个参数表示 切入方法的 [方法反射对象] //第三个参数表示 切入方法的 [参数数组(方法的所有参数组成)] //第四个参数表示 [调用该方法的对象] @Override public void afterReturning(Object returnObject, Method method, Object[] argArray, Object callObject) throws Throwable { System.out.println("add log:print Hello world"); } }
<context:annotation-config /> <context:component-scan base-package="com.shine" /> <bean id="printer" class="com.shine.PrinterImpl"/> <bean id="afterPrinter" class="com.shine.AfterPrinter"/> <!-- 配置一个拦截器 (切入点对象,确定何时何地调用拦截器) --> <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- [通知,特定连接点所采取的动作] --> <!-- 加入切面,切面为当执行完print方法后 再执行加入的切面 --> <property name="advice"> <ref local="afterPrinter"/> </property> <!-- 要拦截的方法,可根据Spring提供匹配方式进行拦截 --> <property name="pattern"> <!-- .表示符合任何单一字元 ### +表示符合前一个字元一次或多次 ### *表示符合前一个字元零次或多次 ### \Escape任何Regular expression使用到的符号 --> <!-- .*表示前面的前缀(包括包名) 表示print方法--> <value>.*print</value> </property> </bean> <!-- ### 代理工程 --> <bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 指定目标对象,目标对象是PrinterImpl对象 --> <property name="target"> <ref local="printer"/> </property> <!-- 该目标中加入拦截器pointcutAdvisor --> <property name="interceptorNames"> <list> <value>pointcutAdvisor</value> </list> </property> </bean>
/** * 测试类 */ public class TestMain { public static void main(String[] args) { ApplicationContext ctx = new FileSystemXmlApplicationContext( "classpath:applicationContext.xml"); // ☆注意:这里的Bean对象获取必须从代理工厂中去取,否则无法切入 IPrinter printer = (IPrinter) ctx.getBean("proxyFactory"); printer.print(); } }
hello world add log:print Hello world
感觉这么多开源东西中,最需要研究的就是Spring了~~ = =|| 继续study~~
No Hibernate Session bound to thread,and configuration does not allow creation错误解决方案
忽遇如下错误:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
百度得解:
因:将对应操作入事务即可
方法:于Spring配置文件的事务拦截器中加入对应的类即可~
出现Unable to find Bean with name XXX 可能原因
今天在公司做项目中,
由Ctrl层 >>> Biz层>>> Dao层 。在Ctrl注入Biz层类 ,在Biz层类注入Dao层类,使用注解的方式进行注入,例如,在Biz层注入Dao层的时候,首先需要将Dao层暴露出来,也就是将这个类标上 @Repository等注解,不然可能会出现Unable to find Bean with name的错误。原理实际上就是定义了一个Bean,没有定义的Bean使用注入自然是找不到这个Bean对象咯~~
换句话说若出现:Unable to find Bean with name 这种错误,不妨看看被注入类是否暴露给其他类咯~~也许是这个问题~
Spring中ApplicationContextAware接口用法
加载Spring配置文件时,如果Spring配置文件中所定义的Bean类,如果该类实现了ApplicationContextAware接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware接口中的
public void setApplicationContext(ApplicationContext context) throws BeansException
方法,并且自动可获得ApplicationContext 对象。前提必须在Spring配置文件中指定改类。
一个Demo程序如下:
Spring配置文件中配置:
<bean id="springContext" class="com.shine.spring.SpringContextHelper"></bean>
/** * ApplicationContext的帮助类 * 自动装载ApplicationContext * * @author ChenST * @create 2010-6-24 * */ public class SpringContextHelper implements ApplicationContextAware { private static ApplicationContext context ; /* * 注入ApplicationContext */ @Override public void setApplicationContext(ApplicationContext context) throws BeansException { //在加载Spring时自动获得context SpringContextHelper.context = context; System.out.println(SpringContextHelper.context); } public static Object getBean(String beanName){ return context.getBean(beanName); } }
Spring中抽象类不能注入属性
昨天做项目的时候,因为注入用的很爽,自然而然的在什么类下都用注入,结果出问题,注入的属性都是为null,查了一下,会不会是抽象类的问题呢,百度之,得下面的答案:
抽象类不能生成实例对象,spring无法注入 。 因为spring的原理是启动服务器时读取配置文件,取得类名后利用反射机制在spring上下文中生成一个单例的对象,由spring注入属性并维护此对象的状态,抽象类在反射生成对象时就已经失败了,后面的不会进行。 |
Spring2.5常用注解
公司代码很多Spring注解,下午花了点时间学习了下。贴个小案例来使用这些注解:
学习的注解列表
@Component |
@Service |
@Repository |
@Controller |
@Autowired |
@Qualifier |
@Resource |
@PostConstruct |
@PreDestroy |
1.水果接口
/** * 水果接口 * * @author ChenST * */ public interface Fruit { /** * 获取水果的名字 * @return */ public String getName(); }
2.苹果类
/** * 苹果 * @author ChenST * */ @Service public class Apple implements Fruit { /* * (non-Javadoc) * @see com.spring.annotation.Fruit#getName() */ public String getName() { return "苹果"; } }
3.梨类
/** * 梨 * @author ChenST * */ @Service public class Pear implements Fruit { public String getName() { return "梨"; } }
4.人类
/** * 人,拥有水果 * @author ChenST * */ /* * @Service表示定义个Bean对象默认Bean的名字为 * 类的第一个字母小写名字,例如Human为human * @Service("name")也可以自己指定名字name * ----------以下四种注解是等效的,只是意义不同 * @Component 比较中立的类进行注释 * @Service 表示服务层 * @Repository表示持久层 * @Controller表示控制层 */ @Service public class Human { /** 表示拥有水果 */ /*@Autowired(required = false)表示不确定 *Spring容器中拥有某个类的Bean时候,这样Spring *找不到这个Bean时也不报错 * *@Autowired注解表示自动注入对象。在配置文件中需要配置 *<context:annotation-config /> *<context:component-scan base-package="*"/> * *@Qualifier("apple")表示当这个fruit有两个实现类时 *指定其中一个。负责会报错 * *@Resource注释与Autowired差不多,Resource可以通过 *name和type来指明要注入的类 */ //@Autowired //@Qualifier("apple") //@Resource(name="apple") @Resource(type=Apple.class) private Fruit fruit; public void setFruit(Fruit fruit) { this.fruit = fruit; } /** * 吃水果 */ public void eat(){ System.out.println("我在吃"+this.fruit.getName()); } /* * @PostConstruct表示Spring容器初始化后调用 */ @PostConstruct public void eatBegin(){ System.out.println("我要开始吃了"); } /* * @PreDestroy表示Spring容器销毁之前调用 */ @PreDestroy public void eatFinish(){ System.out.println("吃完了"); } }
5.测试类
/** * 测试类 * * @author ChenST * */ public class Test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Human human=(Human) ctx.getBean("human"); human.eat(); } }
5.applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- 自动注解配置 --> <context:annotation-config /> <context:component-scan base-package="*" /> </beans>
6.运行结果
我要开始吃了 我在吃苹果 吃完了
Spring中进行测试-Junit框架的扩展
利用Spring来进行测试。首先看看下面几个类。【参照网上的资料】
1.AbstractSpringContextTests 类是针对所有测试情景的类,一般不使用,而是使用他们的之类。
2.AbstractDependencyInjectionSpringContextTests 类继承了AbstractSpringContextTests类是一个针对所有测试的超类,具体依赖于Spring上下文。可支持依赖注入。
3.AbstractTransactionalSpringContextTests 类继承了AbstractDependencyInjectionSpringContextTests类,继承该类的测试用例在spring管理的事务中进行,测试完后对数据库的记录不会造成任何影响。你对数据库进行一些操作后,它会自动把数据库回滚,这样就保证了你的测试对于环境没有任何影响。
测试用例需实现protected abstract String[] getConfigLocations()方法来获取上下文。
1.创建一个测试类的基类
/** * Spring测试的基类 * * @author ChenST * */ public abstract class BaseSpringTest extends AbstractDependencyInjectionSpringContextTests { /** * 获取上下文spring context,该路径是在classpath下的路径 */ public String[] getConfigLocations() { String[] configLocations = { "applicationContext.xml" }; return configLocations; } }
2.测试用例
/** * 测试用例,测试Dao的方法getUserInfo(String userCode) * * @author ChenST * */ public class UsersDaoTest extends BaseSpringTest { /** 用户Dao,注入实现 */ private UsersDAO userDao; public void setUserDao(UsersDAO userDao) { this.userDao = userDao; } /** * 测试getUserInfo()方法,需要以test开头 */ public void testGetUserInfo(){ Users user=userDao.getUserInfo("004"); System.out.println(user.getDepartment().getDepName()); } }
可通过MyEclipse中的Outline透视图中进行单个方法的测试或者整个类的测试。