使用"横切"技术,AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

使用ProxyFactoryBean实现AOP

Spring自己的AOP实现在于ProxyFactoryBean。

接口

package com.shoudongdaili;

public interface IPerson {

    void say();

}

实现类

package com.shoudongdaili;

public class Person3 implements IPerson {

    private String name3;
private int age3; public String getName3() {
return name3;
}
public void setName3(String name3) {
this.name3 = name3;
}
public int getAge3() {
return age3;
}
public void setAge3(int age3) {
this.age3 = age3;
}
@Override
public String toString() {
return "Person3 [name3=" + name3 + ", age3=" + age3 + "]";
} @Override
public void say() {
System.out.println(toString());
} }

通知类

package com.shoudongdaili;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; public class MyAdvice3 implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//arg0 是 目标类的方法 arg1是目标类的入参数 arg2是目标类实例 发生异常则抛给Throwable
System.out.println("before my advice3...");
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!-- 代理前原对象 -->
<bean id="person3" class="com.shoudongdaili.Person3"></bean>

  <!-- 通知类 -->
<bean id="myAdvice3" class="com.shoudongdaili.MyAdvice3"></bean>

  <!-- 代理对象 -->  
<bean id="proxyPerson3" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.shoudongdaili.IPerson"></property>
<property name="target" ref="person3"></property>
<property name="interceptorNames">
<list>
<value>myAdvice3</value>
</list>
</property>
</bean> </beans>

测试类

package com.shoudongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest3 {

    public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/shoudongdaili-bean.xml");
// IPerson的实现类有Person3和proxyPerson3代理类这两个,注意这里是使用proxyPerson3
IPerson person3 = (IPerson) context.getBean("proxyPerson3");
person3.say();
} }

源代码解读

  然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:

public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
@Override
public Object getObject() throws BeansException {
   //重点一
initializeAdvisorChain();
if (isSingleton()) {
  //重点二
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
}

  重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。

  this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。

  如下:

    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
} if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
} // Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
} // Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
} if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
} else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
} this.advisorChainInitialized = true;
}

  这中间页经过了Advice到Advisor的转换,如下:

    private void addAdvisorOnChainCreation(Object next, String name) {
// We need to convert to an Advisor if necessary so that our source reference
// matches what we find from superclass interceptors.
Advisor advisor = namedBeanToAdvisor(next);
if (logger.isTraceEnabled()) {
logger.trace("Adding advisor with name '" + name + "'");
}
addAdvisor(advisor);
}
    private Advisor namedBeanToAdvisor(Object next) {
try {
return this.advisorAdapterRegistry.wrap(next);
}
catch (UnknownAdviceTypeException ex) {
// We expected this to be an Advisor or Advice,
// but it wasn't. This is a configuration error.
throw new AopConfigException("Unknown advisor type " + next.getClass() +
"; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
"which may also be target or TargetSource", ex);
}
}
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
}

这个包裹过程已经见过很多遍了,采用了适配器的模式。

之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象。

具体JdkDynamicAopProxy和CglibAopProxy的区别联系,参阅java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总

这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。

使用DefaultAdvisorAutoProxyCreator实现自动代理完成AOP

接口

package com.zidongdaili;

public interface IPerson4 {

    void sayhi();

}

实现类

package com.zidongdaili;

public class Person4 implements IPerson4 {

    private String name4;
private int age4; public String getName4() {
return name4;
}
public void setName4(String name4) {
this.name4 = name4;
}
public int getAge4() {
return age4;
}
public void setAge4(int age4) {
this.age4 = age4;
}
@Override
public String toString() {
return "Person4 [name4=" + name4 + ", age4=" + age4 + "]";
} @Override
public void sayhi() {
System.out.println(toString());
} }

通知类

package com.zidongdaili;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice; public class MyAdvice4 implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//arg0 是 目标类的方法 arg1是目标类的入参数 arg2是目标类实例 发生异常则抛给Throwable
System.out.println("before my advice4...");
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="person4" class="com.zidongdaili.Person4"></bean> <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不写 -->
<property name="pattern">
<value>.*say.+</value> <!-- 业务实现方法名匹配 -->
</property>
<property name="advice">
<ref bean="myAdvice4" />
</property>
</bean>

  <!-- 自动代理 -->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean> </beans>

测试类

package com.zidongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest4 {

    public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/zidongdaili-bean.xml"); IPerson4 person4 = (IPerson4) context.getBean("person4");
person4.sayhi(); } }

使用BeanNameAutoProxyCreator实现自动代理完成AOP

BeanNameAutoProxyCreator是自动代理创建器的三种(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)之一.它是根据拦截器和设置的Bean的名称表达式做匹配来创建代理.下面是个例子

1.主要依赖(略)

2.声明一个环绕通知(拦截器)

public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(getClass()+"调用方法前");
Object ret=invocation.proceed();
System.out.println(getClass()+"调用方法后");
return ret;
}
}

3.要创建代理的目标类与接口

public interface UserService {
void print();
}
public class UserServiceImpl implements UserService {
public void print(){
System.out.println(getClass()+"#print");
}
}

4.配置

@Configuration
public class AppConfig {
//要创建代理的目标Bean
@Bean
public UserService userService(){
return new UserServiceImpl();
}
//创建Advice或Advisor
@Bean
public Advice myMethodInterceptor(){
return new MyMethodInterceptor();
}
//使用BeanNameAutoProxyCreator来创建代理
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator=new BeanNameAutoProxyCreator();
//设置要创建代理的那些Bean的名字
beanNameAutoProxyCreator.setBeanNames("userSer*");
//设置拦截链名字(这些拦截器是有先后顺序的)
beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
return beanNameAutoProxyCreator;
}
}

5.测试

public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService= applicationContext.getBean(UserService.class);
userService.print();
}
}

源码分析

BeanNameAutoProxyCreator是一个BeanPostProcessor.它在Bean实例化随后,调用回调org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization进行后期处理来完成代理的创建.
其中AbstractAutoProxyCreator是BeanNameAutoProxyCreator的超类,BeanNameAutoProxyCreator没有重写postProcessAfterInitialization方法.下面看看这个方法:

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
//关键代码在这里
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

再看看wrapIfNecessary方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
} //这个bean是否匹配要创建代理也是在这个方法.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//关键代码在这里
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

再看看createProxy方法:

protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//关键代码看这里
return proxyFactory.getProxy(getProxyClassLoader());
}

再看看org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)如下:

public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

再看看org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy

public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

再看看createAopProxy方法

protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}

剩下的就与ProxyFactoryBean创建代理类似了.

手动实现自动代理实现AOP

我们也可以写一个类,来实现DefaultAdvisorAutoProxyCreator自动代理的功能!

首先,我们需要实现一个接口,也就是BeanPostProcessor接口。
BeanPostProcessor接口作用是:如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。

而我们想要在原型对象bean被创建之后就代理了,就必须在原来的容器中拿到原来的原型对象,需要拿到原来spring容器中的切面对象,这个时候,我们就需要原来的容器,这个时候就需要另一个接口,也就是ApplicationContextAware接口!

通过这2个接口,我们就可以实现自动代理了。

package cn.hncu.xmlImpl;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{
private ApplicationContext applicationContext=null; //bean创建之前调用
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;//在这里,我们直接放行
} //bean创建之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
ProxyFactoryBean factory = new ProxyFactoryBean();
//把原型对象放入代理工厂
factory.setTarget(bean);
//在这里
Advisor adv = applicationContext.getBean(Advisor.class);
factory.addAdvisor(adv);
//返回被代理后的对象
return factory.getObject();
} //拿到原来的spring中的容器
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="person4" class="com.zidongdaili.Person4"></bean> <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不写 -->
<property name="pattern">
<value>.*say.+</value> <!-- 业务实现方法名匹配 -->
</property>
<property name="advice">
<ref bean="myAdvice4" />
</property>
</bean> <!-- 自己写的自动代理 -->
<bean class="cn.hncu.xmlImpl.MyAutoProxy"></bean> </beans>

Spring里的aop实现方式和源码分析的更多相关文章

  1. Spring第四天,BeanPostProcessor源码分析,彻底搞懂IOC注入及注解优先级问题!

  2. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  3. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  4. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  5. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  6. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  7. 涨姿势:Spring Boot 2.x 启动全过程源码分析

    目录 SpringApplication 实例 run 方法运行过程 总结 上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入 ...

  8. Spring Boot 2.x 启动全过程源码分析

    Spring Boot 2.x 启动全过程源码分析 SpringApplication 实例 run 方法运行过程 上面分析了 SpringApplication 实例对象构造方法初始化过程,下面继续 ...

  9. Android Debuggerd 简要介绍和源码分析(转载)

    转载: http://dylangao.com/2014/05/16/android-debuggerd-%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E5%92%8C%E ...

随机推荐

  1. socketio server推送

    假设面试官问你:要把server端的数据时时显示在浏览器上怎么实现?我想有非常多人会回答使用Ajax技术定时去訪问一个资源,没错,使用Ajax的确能实现.但面试官要的绝对不是这个答案. 由于使用Aja ...

  2. [CSS] Pseduo

    #self aside li{ list-style-type: none;padding:5px;border-bottom: 1px solid #ccc;} #self aside li:las ...

  3. ArcGIS中的查询

    最近身体不适,静下心来看了一下以前收集的电子书.下面是<ArcGIS地理信息系统教程_第5版>(李玉龙)第5章“查询”的读书笔记. 1.查询的常见应用: 选择感兴趣的要素:查找哪些要素满足 ...

  4. objective-c 字符串基本操作

    .定义一个字符串a, 截取a 的某一个部分,复制给b, b必须是int型 NSString *a = @"1.2.30"; ,)] intValue]; NSLog(@" ...

  5. Android 四大组件之 Service(一)

    Service是Android中四大组件之一,在Android开发中起到非常重要的作用,它运行在后台,不与用户进行交互. 1.Service的继承关系: java.lang.Object → andr ...

  6. php之快速入门学习-3(print和echo)

    PHP echo 和 print 语句 echo 和 print 区别: echo - 可以输出一个或多个字符串 print - 只允许输出一个字符串,返回值总为 1 提示:echo 输出的速度比 p ...

  7. TQ2440烧写方法总结

    USB烧写 接线:需要连接串口线.USB下载线和电源线. 软件:USB转串口驱动(因为现在的笔记本上一般都没有串口).USB下载驱动和DNW. 网络烧写 接线:需要连接串口线.网线和电源线. 软件:T ...

  8. java创建线程的三种方式及其对比

    第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...

  9. 算法笔记_112:第五届蓝桥杯软件类省赛真题(Java本科B组)试题解答

     目录 1 武功秘籍 2 切面条 3 猜字母 4 大衍数列 5 圆周率 6 奇怪的分式 7 扑克序列 8 分糖果 9 地宫取宝 10 矩阵翻硬币   前言:以下试题解答代码部分仅供参考,若有不当之处, ...

  10. js setInterval() 用法示例

      Created by Marydon on 1.定义 语法:setInterval(param1,param2) param1 要调用的函数或要执行的代码串. param2 周期性调用param1 ...