Android开发中,自定义注解的两种应用方式
Posted 不入流Android开发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发中,自定义注解的两种应用方式相关的知识,希望对你有一定的参考价值。
java注解在android开发中主要有两种使用方式;一种是在程序运行期间获取类的信息进行反射调用;另一种是使用注解处理,在编译期间生成相关代码,然后在运行期间通过调用这些代码来实现相关功能。
我们先了解一下注解的分类和关键字
从上图中可以看出java中的注解主要分为两类,分别是标准注解和元注解。标准注解是 Java 为我们提供的预定义注解,这个我们没多大关系,主要是元注解,元注解是用来提供给用户自定义注解用的,接下来我们来学习一下元注解。
JAVA 元注解
先解释每个注解的含义:
1.元注解之Target
@Target: 注解的作用:
- @Target(ElementType.TYPE) //类、接口、枚举、注解
- @Target(ElementType.FIELD) //类成员(构造方法、方法、成员变量)
- @Target(ElementType.METHOD) //方法
- @Target(ElementType.PARAMETER) //方法参数
- @Target(ElementType.CONSTRUCTOR) //构造函数
- @Target(ElementType.LOCAL_VARIABLE)//局部变量
- @Target(ElementType.ANNOTATION_TYPE)//注解
- @Target(ElementType.PACKAGE)//包
- @Target(ElementTypeTYPE_PARAMETER) //类型参数声明
- @Target(ElementType.TYPE_USE) //使用类型
2.元注解之Retention
@Retention:注解的保留位置
- @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含,注解将被编译器丢弃.
- @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,会被JVM丢弃.
- @Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
3.元注解之Document
- @Document:说明该注解将被包含在javadoc中
4.元注解之Inherited
- @Inherited:说明子类可以继承父类中的该注解
5.元注解之Repeatable
- @Repeatable:在需要对同一种注解多次使用应用于指定对象,往往需要借助@Repeatable,为java8新增。
了解完这些注解的含义,我们来自定义一个,java注解有两种实现方式。
JAVA自定义注解
1.基于反射使用注解
首先我们的目标是制作一个用户信息表,是一个User对象,表中有其对应的属性,将注解和属性等关联,然后再通过反射拿到对应的注解值和属性值打印,思路如下:
1.1. 首先定义两个注解UserAnnotation和UserAttribute,创建一个注解遵循: public @interface 注解名 方法参数
1.2. 将注解和User对象关联给用户赋值。
1.3. 获取注解,打印注解的注解值和属性值。
定义注解代码如下
将注解和User对象绑定
主要通过获取注解打印
public static void main(String [] args)
UserBean mUserBean =new UserBean();
mUserBean.setUserName("张三");
mUserBean.setUserSex("男");
mUserBean.setUserAge(18);
StringBuffer mStringBuffer= printData(mUserBean);
System.out.println(mStringBuffer);
我们主要看一下printData这个打印的代码,都有注释不解释
private static StringBuffer printData(UserBean userBean)
//创建一个StringBuffer对象拼接数据
StringBuffer mStringBuffer =new StringBuffer();
//根据对象获取注解Class
Class annotationClass =userBean.getClass();
//判断对象中有没有我们定义的userAnnotation注解
boolean bUserAnnotation =annotationClass.isAnnotationPresent(CustomeAnnotation.UserAnnotation.class);
if(bUserAnnotation)
//获取userAnnotation注解
CustomeAnnotation.UserAnnotation mUserAnnotation= (CustomeAnnotation.UserAnnotation) annotationClass.getAnnotation(CustomeAnnotation.UserAnnotation.class);
//获取获取userAnnotation注解的值
String userAnnotationName =mUserAnnotation.userClassName();
mStringBuffer.append(userAnnotationName+"信息如下:");
//进而获取对象中的所有属性
//获取对象中的所有属性
Field[] fields = annotationClass.getDeclaredFields();
//便遍历属性
for(Field field : fields)
//判断属性中是否有我们定义的UserAttribute
boolean bUserAttribute =field.isAnnotationPresent(CustomeAnnotation.UserAttribute.class);
if(bUserAttribute)
//获取自定义UserAttribute注解
CustomeAnnotation.UserAttribute userAttribute = field.getAnnotation(CustomeAnnotation.UserAttribute.class);
//获取去userAttribute中的值
String name = userAttribute.userAttribute();
Object value = "";
try
//获取对应属性的值,toUpperCase()把字符串转换为大写
Method method = annotationClass.getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
value = method.invoke(userBean);
catch (Exception e)
e.printStackTrace();
//string类型
if (value instanceof String)
mStringBuffer.append(name + "=").append(value).append(",");
else if (value instanceof Integer)
mStringBuffer.append(name + "=").append(value).append(",");
else
throw new NullPointerException("UserAttribute not find");
else//如果不存在抛一个异常
throw new NullPointerException("userAnnotation not find");
return mStringBuffer;
这样的话我们就可以成功打印出信息了,请问明白了没,如果没有明白,没关系我们继续在写一个,类似Butterknife的:
- 第一步定义注解
- 第二步将注解应用于控件
- 在App运行时,通过反射将findViewbyId得到的控件,注入到注解描述的成员变量中完成绑定。
第一步:
第二步:
第三步:
2. 基于 annotationProcessor 使用注解
这个看到过一篇文章比较专业,我就不再这里说了,详细知识点可以查看这份Android高级进阶学习手册。
spring 中常用的两种事务配置方式以及事务的传播性隔离级别
一、注解式事务
1、注解式事务在平时的开发中使用的挺多,工作的两个公司中看到很多项目使用了这种方式,下面看看具体的配置demo。
2、事务配置实例
(1)、spring+mybatis 事务配置
<!-- 定义事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--使用注释事务 --> <tx:annotation-driven transaction-manager="transactionManager" />
(2)、spring+hibernate 事务配置
<!-- 事务管理器配置,单数据源事务 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
看到上面的这两段配置文件是不是很熟悉,对的这就是我们平时看到的事务的配置,在spring的配置中配置数据源即dataSource、事务管理器,事务管理器使用不同的orm框架事务管理器类就不同,比如这里使用的是mybatis 所以是
org.springframework.jdbc.datasource.DataSourceTransactionManager
如果使用hibernate 则事务管理器类为
org.springframework.orm.hibernate3.HibernateTransactionManager
这是使用注解方式时要配置的,代码中的具体的注解以及事务的传播性、隔离级别一般在service 层中配置下面看看
3、@Transactional
(1)、这里说明一下,有的把这个注解放在类名称上面了,这样你配置的这个@Transactional 对这个类中的所有public方法都起作用
(2)、@Transactional 方法方法名上,只对这个方法有作用,同样必须是public的方法
比如这里就对这个方法定义了一个事务同时设置了很多属性:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT) public void saveUser(Map<String, String> map) throws Exception { System.out.println("方法开始"); for (int i = 0; i < 500000; i++) { System.out.println("*"); } System.out.println("进入保存"); userDao.saveUser(map); System.out.println("退出保存"); }
4、事物配置中有哪些属性可以配置
(1)、事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
(2)、事务的超时性:@Transactional(timeout=30) //默认是30秒
注意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的
(3)、事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
(4)、回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
(5)、只读:@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
ok 这种注解方式实现事务的配置以及一些属性的定义,其实事务的东西还有很多要注意的事项,如果要深入学习的话要学习的东西还很多,这里只是简单记录一下
那我们要注意什么那:
1、在spring配置文件中引入<tx:>命名空间:xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
2、@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
3、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}
4、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
5、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。
6、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
二、使用AOP的方式实现事务的配置
1、这种事务使用也很多,下面我们来看看简单的例子
2、事务配置实例:
(1)、配置文件
<!-- 定义事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 下面使用aop切面的方式来实现 --> <tx:advice id="TestAdvice" transaction-manager="transactionManager"> <!--配置事务传播性,隔离级别以及超时回滚等问题 --> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="*" rollback-for="Exception" /> </tx:attributes> </tx:advice> <aop:config> <!--配置事务切点 --> <aop:pointcut id="services" expression="execution(* com.website.service.*.*(..))" /> <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" /> </aop:config>
上面我们看到了,简单的配置了事务,其中tx:attributes中设置了事务的传播性,隔离级别以及那种问题能进行回滚超时等这些问题,也就是你自己按照业务需求定制一个事务来满足你的业务需求。
注意: 这里注意一下,在tx:method中配置了rollback_for 中配置的Exception 这个是运行时的异常才会回滚不然其他异常是不会回滚的!
@Service("userService") public class UserService { @Autowired private UserDao userDao; public void saveUser(Map<String, String> map) throws Exception { userDao.saveUser(map); throw new RuntimeException(); // throw new Exception (); } }
这里我们看到了,如果抛出的是一个运行时异常则会回滚而如果抛出的不是运行时异常则会不回滚的。
需要注意的地方:
(1)、在spring 配置文件中引入:xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
(2) advice(建议)的命名:由于每个模块都会有自己的Advice,所以在命名上需要作出规范,初步的构想就是模块名+Advice(只是一种命名规范)。
(3) tx:attribute标签所配置的是作为事务的方法的命名类型。
如<tx:method name="save*" propagation="REQUIRED"/>
其中*为通配符,即代表以save为开头的所有方法,即表示符合此命名规则的方法作为一个事务。
propagation="REQUIRED"代表支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
(4) aop:pointcut标签配置参与事务的类,由于是在Service中进行数据库业务操作,配的应该是包含那些作为事务的方法的Service类。
首先应该特别注意的是id的命名,同样由于每个模块都有自己事务切面,所以我觉得初步的命名规则因为 all+模块名+ServiceMethod。而且每个模块之间不同之处还在于以下一句:
expression="execution(* com.test.testAda.test.model.service.*.*(..))"
其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。
(5) aop:advisor标签就是把上面我们所配置的事务管理两部分属性整合起来作为整个事务管理。
(6)注意标红的地方
ok 到这里两种配置方式简单的demo 都有了,下面我们来看看tx:method 中还有那些属性可以配置
下面来看看aop 这种方式来配置的时候我们还能配置那些属性:
<tx:advice id="advice" transaction-manager="txManager"> <tx:attributes> <!-- tx:method的属性: * name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:\'get*\'、\'handle*\'、\'on*Event\'等等. * propagation 不是必须的 ,默认值是REQUIRED 表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED * isolation 不是必须的 默认值DEFAULT 表示事务隔离级别(数据库的隔离级别) * timeout 不是必须的 默认值-1(永不超时) 表示事务超时的时间(以秒为单位) * read-only 不是必须的 默认值false不是只读的 表示事务是否只读? * rollback-for 不是必须的 表示将被触发进行回滚的 Exception(s);以逗号分开。 如:\'com.foo.MyBusinessException,ServletException\' * no-rollback-for 不是必须的 表示不被触发进行回滚的 Exception(s);以逗号分开。 如:\'com.foo.MyBusinessException,ServletException\' 任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚 --> <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/> <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/> <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/> <!-- 其他的方法之只读的 --> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice>
OK 两种配置方式我们也都简单学习了,两种配置方式有哪些属性要配置我们也了解了,但是,这里只是说了有这两种常用的方法,而没有具体说事务以及事务的配置带来的数据的安全性以及性能的影响,其实事务不是那么简单,具体深入学习希望以后有总结!
下面给大家列出spring事务的几种传播特性:
1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,
则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
Spring事务的隔离级别
1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别,另外四个与JDBC的隔离级别相对应
2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读,它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行,除了防止脏读,不可重复读外,还避免了幻像读。
事务的隔离级别和数据库中是一样的大家可以看数据库事务隔离级别就能明白了但是事务的传播性是个什么鬼貌似之前没听过那下面就来看看
拷贝文章:http://blog.sina.com.cn/s/blog_4b5bc0110100z7jr.html
我们都知道事务的概念,那么事务的传播特性是什么呢?(此处着重介绍传播特性的概念,关于传播特性的相关配置就不介绍了,可以查看spring的官方文档)在我们用SSH开发项目的时候,我们一般都是将事务设置在Service层 那么当我们调用Service层的一个方法的时候它能够保证我们的这个方法中执行的所有的对数据库的更新操作保持在一个事务中,在事务层里面调用的这些方法要么全部成功,要么全部失败。那么事务的传播特性也是从这里说起的。如果你在你的Service层的这个方法中,除了调用了Dao层的方法之外,还调用了本类的其他的Service方法,那么在调用其他的Service方法的时候,这个事务是怎么规定的呢,我必须保证我在我方法里掉用的这个方法与我本身的方法处在同一个事务中,否则如果保证事物的一致性。事务的传播特性就是解决这个问题的,“事务是会传播的”在Spring中有针对传播特性的多种配置我们大多数情况下只用其中的一种:PROPGATION_REQUIRED:这个配置项的意思是说当我调用service层的方法的时候开启一个事务(具体调用那一层的方法开始创建事务,要看你的aop的配置),那么在调用这个service层里面的其他的方法的时候,如果当前方法产生了事务就用当前方法产生的事务,否则就创建一个新的事务。这个工作使由Spring来帮助我们完成的。以前没有Spring帮助我们完成事务的时候我们必须自己手动的控制事务,例如当我们项目中仅仅使用hibernate,而没有集成进spring的时候,我们在一个service层中调用其他的业务逻辑方法,为了保证事物必须也要把当前的hibernate session传递到下一个方法中,或者采用ThreadLocal的方法,将session传递给下一个方法,其实都是一个目的。现在这个工作由spring来帮助我们完成,就可以让我们更加的专注于我们的业务逻辑。而不用去关心事务的问题。默认情况下当发生RuntimeException的情况下,事务才会回滚,所以要注意一下 如果你在程序发生错误的情况下,有自己的异常处理机制定义自己的Exception,必须从RuntimeException类继承 这样事务才会回滚!
以上是关于Android开发中,自定义注解的两种应用方式的主要内容,如果未能解决你的问题,请参考以下文章
实现SpringBoot项目的多数据源配置的两种方式(dynamic-datasource-spring-boot-starter和自定义注解的方式)